useState 中的变量未在 useEffect 回调中更新

新手上路,请多包涵

我在使用 useState 和 useEffect 挂钩时遇到问题

import { useState, useEffect } from "react";

const counter = ({ count, speed }) => {
    const [inc, setInc] = useState(0);

    useEffect(() => {

        const counterInterval = setInterval(() => {
            if(inc < count){
                setInc(inc + 1);
            }else{
                clearInterval(counterInterval);
            }
        }, speed);

    }, [count]);

    return inc;
}

export default counter;

上面的代码是一个计数器组件,它在 props 中接受计数,然后用 0 初始化 inc 并递增它直到等于计数

问题是每次我得到 0 时,我都没有在 useEffect 和 setInterval 的回调中得到 inc 的更新值,所以它将 inc 呈现为 1 并且 setInterval 永远不会变得清晰。我认为公司必须关闭使用 useEffect 和 setInterval 的回调,所以我必须在那里获取更新公司,所以这可能是一个错误?

我不能在依赖项中传递 inc(在其他类似问题中建议这样做),因为在我的例子中,我在 useEffect 中设置了 setInterval,因此在依赖项数组中传递 inc 会导致无限循环

我有一个使用有状态组件的工作解决方案,但我想使用功能组件来实现

原文由 Abhay Sehgal 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 534
2 个回答

有几个问题:

  1. 您没有从 useEffect 返回一个函数来清除间隔
  2. 您的 inc 值不同步,因为您没有使用 inc 的先前值。

一种选择:

 const counter = ({ count, speed }) => {
    const [inc, setInc] = useState(0);

    useEffect(() => {
        const counterInterval = setInterval(() => {
            setInc(inc => {
                if(inc < count){
                    return inc + 1;
                }else{
                    // Make sure to clear the interval in the else case, or
                    // it will keep running (even though you don't see it)
                    clearInterval(counterInterval);
                    return inc;
                }
            });
        }, speed);

        // Clear the interval every time `useEffect` runs
        return () => clearInterval(counterInterval);

    }, [count, speed]);

    return inc;
}

另一种选择是将 inc 包含在 deps 数组中,这使事情变得更简单,因为您不需要使用之前的 inc 内部 setInc :901af-51

 const counter = ({ count, speed }) => {
    const [inc, setInc] = useState(0);

    useEffect(() => {
        const counterInterval = setInterval(() => {
            if(inc < count){
                return setInc(inc + 1);
            }else{
                // Make sure to clear your interval in the else case,
                // or it will keep running (even though you don't see it)
                clearInterval(counterInterval);
            }
        }, speed);

        // Clear the interval every time `useEffect` runs
        return () => clearInterval(counterInterval);

    }, [count, speed, inc]);

    return inc;
}

还有 第三种 方法更简单:包括 inc 在 deps 数组中,如果 inc >= count ,在调用之前提前返回 setInterval

     const [inc, setInc] = useState(0);

    useEffect(() => {
        if (inc >= count) return;

        const counterInterval = setInterval(() => {
          setInc(inc + 1);
        }, speed);

        return () => clearInterval(counterInterval);
    }, [count, speed, inc]);

    return inc;

原文由 helloitsjoe 发布,翻译遵循 CC BY-SA 4.0 许可协议

这里的问题是来自 clearInterval 的回调在每次 useEffect 运行时定义,即 count 更新。定义时的值 inc 将在回调中读取。

这个编辑有一个不同的方法。我们包含一个 ref 来跟踪 inc 小于 count ,如果小于我们可以继续递增 inc 。如果不是,那么我们会清除计数器(就像您在问题中所做的那样)。每次 inc 更新,我们评估它是否仍然小于计数并将其保存在 ref 中。然后在前面的 useEffect 中使用该值。

正如@DennisVash 在他的回答中正确指出的那样,我包括了对 speed 的依赖。

 const useCounter = ({ count, speed }) => {
    const [inc, setInc] = useState(0);
    const inc_lt_count = useRef(inc < count);

    useEffect(() => {
        const counterInterval = setInterval(() => {
            if (inc_lt_count.current) {
                setInc(inc => inc + 1);
            } else {
                clearInterval(counterInterval);
            }
        }, speed);

        return () => clearInterval(counterInterval);
    }, [count, speed]);

    useEffect(() => {
        if (inc < count) {
            inc_lt_count.current = true;
        } else {
            inc_lt_count.current = false;
        }
    }, [inc, count]);

    return inc;
};

原文由 Alvaro 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题