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

Javascript 反应:通过setTimeout改变状态返回错误结果,javascript,reactjs,Javascript,Reactjs,我有一系列警报作为我的状态 我正试图让我的用户通过点击手动解除警报,几秒钟后也会自动解除警报 目前,只有第三个警报自动清除,其余警报保持不变 通过当前的实施,我甚至可以实现我的目标吗? 如果没有,什么是正确的探索方向 import React, { useState, useEffect } from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const [aler

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

我正试图让我的用户通过点击手动解除警报,几秒钟后也会自动解除警报

目前,只有第三个警报自动清除,其余警报保持不变

通过当前的实施,我甚至可以实现我的目标吗? 如果没有,什么是正确的探索方向

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);
import React,{useState,useffect}来自“React”;
从“react dom”导入react dom;
导入“/styles.css”;
函数App(){
const[alerts,setAlerts]=useState([
{信息:“警报1”},
{信息:“警报2”},
{信息:“警报3”},
{信息:“警报4”}
]);
const dismissAlert=alertIndex=>{
设置警报([
…警报.slice(0,警报索引),
…警报.slice(警报索引+1)
]);
};
返回(


提前感谢。

查看您的代码的一个修改示例,我认为它或多或少可以按照您的意愿工作,但可能会出现问题。我觉得工作不太好的地方是取消setTimeout上的警报。似乎setTimeout正在删除可能已经删除的内容。因此为了保持同步,我维护了一个queue将保存要删除的项目。此外,我使用了
id
而不是依赖拼接后可以更改的索引。如果需要进一步帮助,请告诉我

顺便说一句,您需要解决
设置超时问题,原因是在渲染它们之后,所有它们都将被安排在一瞬间删除,这将给人一种同时删除它们的感觉

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);
import React,{useState,useffect}来自“React”;
从“react dom”导入react dom;
导入“/styles.css”;
函数App(){
useffect(()=>{
设置间隔(addAlert,5000);
设置间隔(解除警报,6000);
}, []);
常量addAlert=()=>{
设置警报([
…警报,
{id:new Date().getTime(),消息:“new alert”+Math.random()}
]);
};
const[alerts,setAlerts]=useState([
{id:1,消息:“警报1”},
{id:2,消息:“警报2”},
{id:3,消息:“警报3”},
{id:4,消息:“警报4”}
]);
常量[alertsToRemove,addAlertsToRemove]=useState([]);
const queueremovation=alertId=>{
if(alertsToRemove.indexOf(alertId){
alertsToRemove.forEach(id=>{
var alertToDelete=alerts.find(a=>a.id==id);
alerts.splice(alerts.indexOf(alertToDelete));
设置警报(警报);
});
addAlertsToRemove([]);
};
返回(

检查您的代码的一个修改示例,我认为它或多或少可以按照您的意愿工作,但可能会出现问题。我觉得工作不太好的地方是取消setTimeout上的警报。似乎setTimeout正在删除可能已删除的内容。因此,为了保持它的并发性,我维护了一个队列,该队列将保留要删除的项目。此外,我使用了
id
而不是依赖拼接后可以更改的索引。如果需要进一步帮助,请告诉我

顺便说一句,您需要解决
设置超时问题,原因是在渲染它们之后,所有它们都将被安排在一瞬间删除,这将给人一种同时删除它们的感觉

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);
import React,{useState,useffect}来自“React”;
从“react dom”导入react dom;
导入“/styles.css”;
函数App(){
useffect(()=>{
设置间隔(addAlert,5000);
设置间隔(解除警报,6000);
}, []);
常量addAlert=()=>{
设置警报([
…警报,
{id:new Date().getTime(),消息:“new alert”+Math.random()}
]);
};
const[alerts,setAlerts]=useState([
{id:1,消息:“警报1”},
{id:2,消息:“警报2”},
{id:3,消息:“警报3”},
{id:4,消息:“警报4”}
]);
常量[alertsToRemove,addAlertsToRemove]=useState([]);
const queueremovation=alertId=>{
if(alertsToRemove.indexOf(alertId){
alertsToRemove.forEach(id=>{
var alertToDelete=alerts.find(a=>a.id==id);
alerts.splice(alerts.indexOf(alertToDelete));
设置警报(警报);
});
addAlertsToRemove([]);
};
返回(

您可能需要另一种方法来识别需要解除的警报,因为索引可能会在请求解除与实际发生解除之间发生变化。显然,React在更新
警报时没有清除my
setTimeout
,因为它得到的是相同的
(我想这就是为什么
eslint
总是抱怨索引是键的原因)将
键更改为
i+alert.message
修复了我的问题。我认为它有另一个错误。4条消息应该在同一时间清除。但它清除顺序是的,因为
警报
重新加载并在加载时获得一个新的
设置超时
,我现在正试图用
React.memo
解决这个问题。您可能需要一个备选方案这是一种识别需要解除警报的有效方法,因为索引可能会在请求解除警报的时间和实际发生解除警报的时间之间发生变化。显然,React在更新
警报时没有清除my
setTimeout
,因为它得到的是相同的
(我想这就是为什么
eslint
总是抱怨索引是键的原因)将
键更改为
i+alert.message
修复了我的问题。我认为它有另一个错误。4条消息应该在同一时间清除。但它清除顺序是的,因为
警报
重新加载并在加载时获得新的
设置超时
,我现在正试图用
React.memo
解决这个问题。