![](/img/trans.png)
[英]React - How to update the state in useEffect without causing an infinite loop?
[英]How To Pass State Update To Custom Render Loop In React's `useEffect`?
我经常遇到这样的情况,我构建了一个基于window.requestAnimationFrame
的渲染循环,但是想要在循环内部交换一些状态。 有几种方法可以做到这一点,但我不确定哪种方法最好。 我的设置通常是这样的:
function Animation({someState}) {
const loopDataRef = useRef();
useEffect(() => {
//do something initialization work for the renderloop, e.g. getContext, etc.
let frame;
const loop = (cts) => {
frame = window.requestAnimationFrame(loop)
//do some loop work using someState and loopDataRef.current
}
loop();
return () => window.cancelAnimationFrame(frame);
}, []);
}
请注意,在循环中使用someState
将始终使用效果的最后一次运行的值,不一定是当前值。 以下是一些显而易见的解决方案:
我可以将someState
放在 useEffect 的依赖数组中,这样只要useEffect
发生变化,循环就会重新启动。 但是如果初始化很昂贵,例如使用 WebGL,我在那里创建所有纹理并编译着色器,它似乎不是很优雅。
我可以使用两种效果,一种用于循环本身的初始化,另一种仅用于循环,它在每次 state 更改时停止和启动。 但我仍然认为,理想情况下,animation 循环应该一直运行到停止,而不是在其间停止/启动。
另一个具有两种效果的解决方案,但这次我执行const someStateRef = useRef()
然后在其依赖数组中使用someState
创建一个效果,它只是将someState
写入someStateRef.current
因此我可以在循环中使用它。 这是我实现它的示例。 或者,可以在每次渲染时将someState
写入someStateRef.current
,而不会产生其他影响。 看起来非常高效,但不是很优雅。
最 React 的方式是什么?
我将 go 与您已经实施的第三个选项,因为它看起来与 React Hooks FAQ(第三个代码块)中的一个示例完全一样:
function Example(props) {
// Keep latest props in a ref.
const latestProps = useRef(props);
useEffect(() => {
latestProps.current = props;
});
useEffect(() => {
function tick() {
// Read latest props at any time
console.log(latestProps.current);
}
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []); // This effect never re-runs
}
(将set/clearInterval
替换为request/cancelAnimationFrame
)
为了完整起见:前两个选项存在您提到的缺陷,不建议在每次渲染时将someStateRef.current
写入someState
而没有其他效果。
避免在渲染期间读取和更新引用,因为这会使组件的行为难以预测和理解。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.