1

在我的应用程序中,我在单击按钮时使用来自 useReducer 钩子的调度,在同一个函数中,我使用了 2 秒的 setTimeout。但是当我使用 usereducer 的调度存储数据时,我没有在 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"));


在上面的示例中,我使用调度将数据存储在减速器中,并且我在 2 秒后在按钮单击上调用 console.log("ButtonClick") 但即使在 2 秒后,我也没有在控制台中获取更新的数据。但是在 console.log("Render") 我得到更新的数据。

现场示例:https ://codepen.io/aadi-git/pen/yLJLmNa

4

1 回答 1

1

你打电话时

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);
};

这就是发生的事情:

  1. 使用对象运行dispatch以存储一些数据。这个函数是异步执行的,所以结果不是立即可用的。

  2. 注册一个超时处理程序,它将当前值记录state.data到控制台。由于前面dispatch的工作仍在进行中,所以 的值state.data仍然是旧的。

    这意味着您无法通过在调用console.log后运行来记录新的分派值,dispatch因为您看不到未来。由于状态变化,您只能在重新渲染组件后记录新数据。然后你可以而且应该使用

    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编辑,新的状态被计算并Demo重新渲染。有了这个,一个新的state.data和一个新handleClick的就被创造出来了。随着新的创建,handleClick还创建了一个新函数,该函数被传递给setTimeout. 然后将整个handleClick函数作为元素的onClick处理程序传递。button

重新渲染现已结束,但之前的任务仍处于待处理状态。现在,当超时持续时间结束时(重新渲染组件很久之后),任务将从任务队列中取出并执行。任务仍然引用之前渲染中的函数,并且该函数仍然引用state.data之前渲染中的值。所以记录到控制台的值仍然是之前渲染的旧值。

于 2020-10-09T07:16:17.570 回答