微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在处理程序函数中的 setInterval 中使用 React 状态变量

如何解决如何在处理程序函数中的 setInterval 中使用 React 状态变量

我的代码类似于下面的代码(我删减了大部分以突出问题)。我试图在单击按钮时调用一个函数,该函数调用 setInterval 来增加父组件的状态。不过,记录的内容只是“0”。 “setPercent”调度函数似乎工作正常 - 在调用它之后我知道“百分比”正在更新为 1,但是当它在下一个时间间隔返回时,控制台再次记录 0 并且集合再次设置为 1。我假设正在发生的事情是,当创建“clickHandler”函数时,它很难复制“百分比”的值而不是引用它——我不知道为什么以及如何修复它。非常感谢任何帮助!

const Component = () => {
    const [percent,setPercent] = useState(0)

    const clickHandler = (time) => {
        let timer = setInterval(() => {
            if (percent < 100) {
                console.log('current percent',percent)
                setPercent(percent + 1)
            } else {
                clearInterval(timer)
            }
        },time / 100)
    }

    return (
        <div>
            <Button onClick={clickHandler} />
        </div>
    )
}

解决方法

setState 的函数形式在这里应该很有用。当给定一个函数时,setState 以当前值作为参数调用该函数(而不是通过闭包可访问的旧值)。以下应该有效:

const Component = () => {
    const [percent,setPercent] = useState(0);

    const clickHandler = (time) => {
        let timer = setInterval(() => {
            setPercent((current) => {
                if (current < 100) {
                    console.log('current percent',current)
                    return current + 1;
                } else {
                    clearInterval(timer);
                    return current;
                }
            });
        },time / 100);
    }

    return (
        <div>
            <Button onClick={clickHandler} />
        </div>
    )
}

虽然在 clearInterval 函数中具有 setState 之类的副作用,但感觉并不理想。这是使用 useEffect 停止计时器的不同方法,它也强制使用单个计时器(即使按钮被多次点击):

const Component = () => {
    const [percent,setPercent] = useState(0);
    const [timer,setTimer] = useState();

    // Stop running timer when we reach 100%
    useEffect(() => {
        if (percent >= 100 && timer != null) {
            clearInterval(timer);
            setTimer(null);
        }
    },[percent,timer]);

    const clickHandler = (time) => {
        // Stop any existing timer
        if (timer) {
            clearTimer(timer);
        }
        // Start new timer
        setTimer(setInterval(() => {
            setPercent((current) => current + 1);
        },time / 100));
    }

    return (
        <div>
            <Button onClick={clickHandler} />
        </div>
    )
}
,

这里发生的一些事情可能会让你绊倒。

首先,每次百分比更改时,您的组件都会重新渲染。这可能导致运行多个间隔。 其次,在每次重新渲染时都会创建一个百分比常量的新实例,这意味着每个时间间隔都在使用它在创建时间间隔时持有的值进行操作,这会导致发送到 useState 的更新值发生冲突。

鉴于这一切,IMO 的最佳方法是使用 useEffectsetTimeout 实现这种性质的计时器/计数器

这是一个用于演示解决方案的快速代码沙箱: https://codesandbox.io/s/cold-voice-cu49u?file=/src/App.js

不要为此感到气馁,我已经落入了这个陷阱,我知道其中的陷阱。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。