Javascript 无法从setTimeout中定义的函数中的useReducer钩子访问更新的数据

Javascript 无法从setTimeout中定义的函数中的useReducer钩子访问更新的数据,javascript,reactjs,react-hooks,settimeout,use-reducer,Javascript,Reactjs,React Hooks,Settimeout,Use Reducer,在我的应用程序中,我在单击按钮时使用useReducer钩子的分派,在相同的函数中,我使用2秒的setTimeout。但当我使用usereducer的dispatch存储数据时,我并没有在setTimeout函数中获得更新值 我无法共享原始代码,但共享出现此问题的另一个演示应用程序的片段 const initialData = { data: "ABC" }; function reducer(state = initialData, action) { switch

在我的应用程序中,我在单击按钮时使用useReducer钩子的分派,在相同的函数中,我使用2秒的setTimeout。但当我使用usereducer的dispatch存储数据时,我并没有在setTimeout函数中获得更新值

我无法共享原始代码,但共享出现此问题的另一个演示应用程序的片段

const initialData = { data: "ABC" };

function reducer(state = initialData, action) {
  switch (action.type) {
    case "STORE":
      return {
        ...state,
        data: action.payload
      };
    default:
      return state;
      break;
  }
}
function Demo() {
  const [state, dispatch] = React.useReducer(reducer, initialData);
  console.log("Render : ",state.data);  //Will be executed on each rendering
  const handleClick = () => {
    dispatch({
      type: "STORE",
      payload: state.data + parseInt(Math.random() * 10)
    });
    setTimeout(() => {
      console.log("ButtonClick : ",state.data); //Will be executed after 2 seconds of dispatching.
    }, 2000);
  };
  return <button onClick={handleClick}>{state.data}</button>;
}
ReactDOM.render(<Demo />, document.getElementById("app"));


const initialData={data:“ABC”};
函数缩减器(状态=初始数据,动作){
开关(动作类型){
案例“商店”:
返回{
……国家,
数据:action.payload
};
违约:
返回状态;
打破
}
}
函数Demo(){
const[state,dispatch]=React.useReducer(reducer,initialData);
console.log(“Render:,state.data);//将在每次渲染时执行
常量handleClick=()=>{
派遣({
类型:“存储”,
有效负载:state.data+parseInt(Math.random()*10)
});
设置超时(()=>{
console.log(“ButtonClick:,state.data);//将在调度2秒后执行。
}, 2000);
};
返回{state.data};
}
ReactDOM.render(,document.getElementById(“app”);
在上面的示例中,我使用dispatch在reducer中存储数据,并在2秒钟后单击按钮时调用console.log(“ButtonClick”),但即使在2秒钟后,我也不会在控制台中获得更新的数据。但是在console.log(“Render”)中,我得到了更新的数据

现场示例:

当您呼叫

const handleClick = () => {
  dispatch({
    type: "STORE",
    payload: state.data + parseInt(Math.random() * 10)
  });
  setTimeout(() => {
    console.log("ButtonClick : ",state.data); //Will be executed after 2 seconds of dispatching.
  }, 2000);
};
情况就是这样:

  • 使用对象运行
    dispatch
    ,以存储一些数据。此函数是异步执行的,因此结果不会立即可用

  • 注册一个超时处理程序,它将当前值
    state.data
    记录到控制台。由于前面的
    调度
    仍在进行中,因此
    state.data
    的值仍为旧值

    这意味着您不能在调用
    dispatch
    后运行
    console.log
    来记录新的调度值,因为您无法预见未来。由于状态更改,只有在重新呈现组件后才能记录新数据。那么你可以也应该使用

    React.useEffect(() => {
      console.log(state.data);
    }, [state.data]);
    
  • 关于
    setTimeout
    以及为什么
    console.log
    在其中记录旧值的更多解释 你用

    setTimeout(() => {
      console.log("ButtonClick : ", state.data);
    }, 2000);
    
    这相当于

    const callback = () => console.log("ButtonClick : ", state.data);
    setTimeout(callback, 2000);
    
    在第一行中,您创建了一个函数(此处名为
    callback
    ),用于打印一些文本。此文本由一个简单字符串和
    state.data
    的值组成。因此,此函数有一个引用变量
    state.data
    。调用与外部状态引用相结合的函数,此闭包确保值
    state.data
    在函数存在期间保持活动状态(不被垃圾收集器装箱)

    在第二行中,使用此函数调用
    setTimeout
    。简化这意味着任务存储在某个位置,该位置声明,在给定的超时之后必须执行此函数。因此,只要此任务没有完成,函数就保持活动状态,变量
    state.data

    同时,在处理任务之前的很长一段时间内,新操作将
    dispatch
    ed,计算新状态并
    Demo
    重新呈现。这样就创建了一个新的
    state.data
    和一个新的
    handleClick
    。通过新创建的
    handleClick
    还创建了一个新函数,该函数传递给
    setTimeout
    。然后将整个
    handleClick
    函数作为
    按钮
    元素的
    onClick
    处理程序传递


    重新渲染现已结束,但以前的任务仍处于挂起状态。现在,当超时持续时间结束时(在重新呈现组件很久之后),任务将从任务队列中取出并执行。任务仍然引用来自之前渲染的函数,此函数仍然引用来自之前渲染的值
    状态。数据
    。因此,记录到控制台的值仍然是以前渲染中的旧值。

    即使调度是异步的,也肯定不会花费2秒来执行。我的setTimeout将在2秒后在其内部运行回调,因此它应该具有最新的值。是的,是的,但不是。阅读我添加的解释,我希望这能更好地解释它,发生了什么。谢谢,正确理解。我们能找到解决办法吗?我的意思是我必须访问setTimeout回调中的数据。目前为了避免这个问题,我使用userefhook并将值存储到其中,而不是使用reducer。以及访问setTimeout回调中的值。这是可行的,但我认为这不是理想的方法。你的意思是,你必须在数据更改两秒钟后用数据记录文本?好的,那么我就用
    React.useffect(()=>setTimeout(()=>doSomeStuffWith(state.data),2000),[state.data])直接在
    演示中
    。但是要注意
    状态。数据在这两者之间更新。