简体   繁体   English

使用 useEffect 循环反应陈旧的 state

[英]React stale state with useEffect loop

Given the following component:给定以下组件:

import { useEffect, useState } from "react"

const Test = () => {
    const [thing, setThing] = useState({ x: 0, y: 0 });
    useEffect(() => {
        gameLoop();
    }, []);

    const gameLoop = () => {
        const p = {...thing};
        p.x++;
        setThing(p);
        setTimeout(gameLoop, 1000);
    }

    return (
        <div>

        </div>
    )
}

export default Test;

Every second a new line is printed into the console.每秒都会在控制台中打印一个新行。 The expected output should of course be:预期的 output 当然应该是:

{ x: 1, y: 0 }
{ x: 2, y: 0 }
{ x: 3, y: 0 }

...etc ...ETC

What is instead printed is:而是打印的是:

{ x: 0, y: 0 }
{ x: 0, y: 0 }
{ x: 0, y: 0 }

I've come to the conclusion as this is because of a stale state.我得出结论,因为这是因为 state 过时。 Some solutions I've found and tried are:我找到并尝试过的一些解决方案是:

  1. The dependency array依赖数组

Adding thing to the dependency array causes it to be called ridiculously fast.将东西添加到依赖数组会导致它被快速调用。 Adding thing.x to the array also does the same.将 thing.x 添加到数组中也是如此。

  1. Changing the setState call更改 setState 调用

One post I found mentioned changing the setState to look like so:我发现一篇文章提到将 setState 更改为如下所示:

setState(thing => {
    return {
        ...thing.
        x: thing.x+1,
        y: thing.y
 });

Which changed nothing.这没有任何改变。

I have found a lot of posts about clocks/stopwatches and how to fix this issue with primitive value states but not objects.我发现了很多关于时钟/秒表的帖子,以及如何用原始值状态而不是对象来解决这个问题。

What is my best course of action to fix the behavior while maintaining that it only runs once a second?在保持它每秒只运行一次的同时修复行为的最佳行动方案是什么?

EDIT: The comments seem to be stating I try something like this:编辑:评论似乎是说我尝试这样的事情:

import { useEffect, useState } from "react"

const Test = () => {
    const [thing, setThing] = useState({ x: 0, y: 0 });
    useEffect(() => {
        setInterval(gameLoop, 1000);
     }, []);
     
     const gameLoop = () => {
        console.log(thing)
        const p = {...thing};
        p.x++;
        setThing(prevP => ({...prevP, x: prevP.x+1}));
     }

    return (
        <div>

        </div>
    )
}

export default Test;

This still prints out incorrectly:这仍然打印不正确:

{ x: 0, y: 0 }
{ x: 0, y: 0 }
{ x: 0, y: 0 }

I think you can use this solution:我认为您可以使用此解决方案:

useEffect(() => {
    setInterval(() => {
      setThing((prev) => ({
        ...prev,
        x: prev.x + 1,
      }));
    }, 1000);
  }, []);

I suggest to control setInterval with the clearInterval when component is destroied;我建议在组件被销毁时使用 clearInterval 控制 setInterval; like this:像这样:

useEffect(() => {
    const interval = setInterval(() => {
      setThing((prev) => ({
        ...prev,
        x: prev.x + 1,
      }));
    }, 1000);
    return () => {
      clearInterval(interval);
    };
  }, []);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM