反应:通过setTimeout改变状态返回错误结果

时间:2019-02-19 10:21:34

标签: javascript reactjs

我有一系列警报作为我的状态。

我正试图允许我的用户通过单击警报来手动关闭警报,并在几秒钟后自动将其关闭。

当前,只有第三个警报会自动清除,其余的则保持不变。

使用当前的实现,我还能实现我的目标吗? 如果不是,什么是正确的探索方向?

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [alerts, setAlerts] = useState([
    { message: "Alert 1" },
    { message: "Alert 2" },
    { message: "Alert 3" },
    { message: "Alert 4" }
  ]);

  const dismissAlert = alertIndex => {
    setAlerts([
      ...alerts.slice(0, alertIndex),
      ...alerts.slice(alertIndex + 1)
    ]);
  };

  return (
    <div className="App">
      <Alerts alerts={alerts} dismissAlert={dismissAlert} />
    </div>
  );
}

const Alerts = ({ alerts, dismissAlert }) => (
  <div className="alerts">
    {alerts.map((alert, i) => (
      <Alert key={i} alert={alert} dismissAlert={() => dismissAlert(i)} />
    ))}
  </div>
);

const Alert = ({ alert, dismissAlert }) => {
  useEffect(() => {
    const timerId = setTimeout(dismissAlert, 1000);

    return () => {
      console.log("clearing timeout");
      clearTimeout(timerId);
    };
  }, []);

  return (
    <div className="alert" onClick={dismissAlert}>
      <p>{alert.message}</p>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Online SandBox

谢谢。

1 个答案:

答案 0 :(得分:0)

检查代码的修改示例,我认为它或多或少可以达到您的预期目的,但是可能会出现故障。我感觉效果不佳,正在关闭setTimeout警报。似乎setTimeout正在删除可能已经删除的东西。因此,为了保持并发状态,我正在维护一个队列,该队列将保留要删除的项目。我还使用了id而不是依赖可以在拼接后更改的索引。让我知道您是否需要进一步的帮助。

根据您需要在setTimeouts上进行锻炼的方式,为什么会在渲染它们后将它们全部排在瞬间之间,这会给人以为它们都是一起删除

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  useEffect(() => {
    setInterval(addAlert, 5000);
    setInterval(dismissAlert, 6000);
  }, []);

  const addAlert = () => {
    setAlerts([
      ...alerts,
      { id: new Date().getTime(), message: "new alert" + Math.random() }
    ]);
  };

  const [alerts, setAlerts] = useState([
    { id: 1, message: "Alert 1" },
    { id: 2, message: "Alert 2" },
    { id: 3, message: "Alert 3" },
    { id: 4, message: "Alert 4" }
  ]);

  const [alertsToRemove, addAlertsToRemove] = useState([]);

  const queueRemoval = alertId => {
    if (alertsToRemove.indexOf(alertId) <= -1) {
      alertsToRemove.push(alertId);
      addAlertsToRemove(alertsToRemove);
    }
  };

  const dismissAlert = () => {
    alertsToRemove.forEach(id => {
      var alertToDelete = alerts.find(a => a.id == id);
      alerts.splice(alerts.indexOf(alertToDelete));
      setAlerts(alerts);
    });
    addAlertsToRemove([]);
  };

  return (
    <div className="App">
      <Alerts alerts={alerts} queueRemoval={queueRemoval} />
    </div>
  );
}

const Alerts = ({ alerts, queueRemoval }) => (
  <div className="alerts">
    {alerts.map((alert, i) => (
      <Alert
        key={i}
        alert={alert}
        queueRemoval={() => queueRemoval(alert.id)}
      />
    ))}
  </div>
);

const Alert = ({ alert, queueRemoval }) => {
  useEffect(() => {
    const timerId = setTimeout(queueRemoval, 3000);

    return () => {
      console.log("clearing timeout");
      clearTimeout(timerId);
    };
  }, []);

  return (
    <div className="alert" onClick={queueRemoval}>
      <p>{alert.message}</p>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit jqo02movw