反应状态正确更新,除了内部游戏循环功能

React state updated properly except inside gameloop function

我有这个非常简单的 React 代码,它 使用 useState 挂钩 声明了一个名为 money 的状态变量。

然后我使用useEffect hook触发一个游戏循环开始一次。

在这个游戏循环中,我只是增加了 money 状态变量的值。

import React, { useState, useEffect } from "react";

export default function Game() {
  // declare one state variable
  const [money, setMoney] = useState(100);

  // start gameloop
  useEffect(() => {
    let anim = requestAnimationFrame(gameloop);

    return () => {
      cancelAnimationFrame(anim);
    };
  }, []);

  function gameloop() {
    setMoney(money => money + 1);
    console.log(money); // always returns 100
    requestAnimationFrame(gameloop);
  }

  return (
    <div>
      <div>{money}</div>
    </div>
  );
}

UI 正确更新,当我使用 React 开发工具查看它时,状态也会更新。

然而,在 gameloop 函数中,当我执行 console.log(money); 它总是打印 100.

似乎如果我尝试读取 gameloop 函数中的 money 变量,它总是初始状态而不是真实状态。

知道我在这里做错了什么吗?

你没有做错任何事。这是因为 React 状态是异步更新的。这意味着当您想要控制台记录 money 变量的状态时,状态尚未更新。你可以做的是从 useEffect 函数中记录它,像这样:

useEffect(() => {
    let anim = requestAnimationFrame(gameloop);
    console.log(money);    
    return () => {
      cancelAnimationFrame(anim);
    };
  }, [money]);

gameloop 函数是在 money 的值为 100 时在初始渲染中声明的。正是这个相同的函数,在其范围内 money 变量仍然是 100,在每个帧上被调用,而不是在每个渲染上创建的函数(它在 [= 中具有 "freshest" 值12=]).

要同步两个计划(rAF 与 React),您可以使用 useRef 挂钩。例如,在每个渲染器上重新创建游戏循环:

let gameloop = useRef();
gameloop.current = function() {
  ...
  requestAnimationFrame(gameloop.current);
}

实际上,useRef 挂钩在函数组件上创建 a sort of instance property