派发后直到下一次渲染才反映最新状态

Posted

技术标签:

【中文标题】派发后直到下一次渲染才反映最新状态【英文标题】:Latest state is not reflected after dispatch until next render 【发布时间】:2020-05-01 17:35:03 【问题描述】:

我在 React 状态管理方面遇到了问题。 我想在单击按钮后使用 Context API 更新全局状态,然后使用更新后的状态执行其他操作。

按钮将动作分派到全局状态。但是,当我尝试在之后立即读取状态时,更改不会反映,直到应用重新渲染。

const App = () => 
  const playerRef = useRef();
  const  state, addPlayers  = useContext(playerContext);

  // subscribe playerRef changes to global state,
  useEffect(() => 
    playerRef.current = state;
    console.log("players #", playerRef.current.length);
  , [state]);

  const add = useCallback(() => 
    // dispatch an add players action to the store
    addPlayers([ name: "new" ,  name: "newer" ]);

    // PROBLEM: Need latest state value here after dispatch
    // but it's not immediately reflected here
    console.log("player # after dispatch ", playerRef.current.length);

    // do something with the new state
  , [playerRef, addPlayers]);

  const handleSubmit = () => 
    add();
  ;

  return (
    <div>
      <p>Players: String(state.map(player => player.name))</p>
      <button type="button" onClick=handleSubmit>
        AddPlayers
      </button>
    </div>
  );
;

export default App;

这是控制台显示的内容。

link 到代码框查看完整的应用程序。 谢谢。

【问题讨论】:

【参考方案1】:

您正试图在状态更新后立即读取 playerRef.current 的值:

  const add = useCallback(() => 
    addPlayers([ name: "new" ,  name: "newer" ]);
    console.log("player # after dispatch ", playerRef.current.length);
  , [playerRef, addPlayers]);

但是,playerRef.current 的值在运行以下效果之前不会更新:

  useEffect(() => 
    playerRef.current = state;
    console.log("players #", playerRef.current.length);
  , [state]);

useCallback 通话结束后最快会发生这种情况*

您应该改为读取state 的值。 Refs 应该用作实例变量来捕获稍后要读取的值;这对他们来说不是一个很好的用例。您试图做的基本上是这样的:

  const  state, addPlayers  = useContext(playerContext);
  const playerRef = React.useRef();
  playerRef.current = state;

也可能是:

  const  state: currentPlayer, addPlayers  = useContext(playerContext);

* 这可能不太准确,但无论哪种方式,您都 这样做取决于竞争条件。

【讨论】:

以上是关于派发后直到下一次渲染才反映最新状态的主要内容,如果未能解决你的问题,请参考以下文章

Apple ZoomingPDFViewer 直到第一次缩放后才允许拖动

状态对 Vuex 没有反应

Teradata 保留价值直到下一次更改

标记直到下一次出现数据

取消当前的触摸事件,直到下一次触摸开始

完成就绪状态后,清除缓存后,JS函数只会运行一次?