我有这个非常简单的反应代码,它使用 useState 钩子声明一个名为 money 的状态变量。
然后我使用 useEffect 钩子触发一个游戏循环启动一次。
在这个游戏循环中,我只是增加货币状态变量的值。
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 开发工具查看它时,状态也会更新。
但是,在游戏循环函数中,当我执行console.log(money);
时,它总是打印 100。
似乎如果我尝试读取游戏循环函数中的 money 变量,它总是初始状态,而不是真实状态。
关于我在这里做错了什么的任何想法?
gameloop
函数是在初始渲染中money
具有值100
时声明的。正是这个相同的函数,在其范围内,money
变量仍然是 100,在每一帧上被调用,而不是在每个渲染上创建的函数(在money
中具有"最新"值(。
要同步两个计划(rAF 与 React(,您可以使用useRef
钩子。例如,在每个渲染上重新创建游戏循环:
let gameloop = useRef();
gameloop.current = function() {
...
requestAnimationFrame(gameloop.current);
}
实际上,useRef
钩子在函数组件上创建一种实例属性。
你没有做错什么。这是因为 React 状态是异步更新的。这意味着,当您想要控制台记录money
变量的状态时,状态尚未更新。您可以做的是从useEffect
函数中记录它,如下所示:
useEffect(() => {
let anim = requestAnimationFrame(gameloop);
console.log(money);
return () => {
cancelAnimationFrame(anim);
};
}, [money]);