欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

react hooks的闭包陷阱(setInterval不生效)问题

程序员文章站 2022-04-06 15:12:37
...

在使用react hooks时, 会遇到这样的问题

const [count, setCount] = useState(1)
useEffect(() => {
    setInterval(() => {
        console.log(count)
    }, 1000)
        //闭包陷阱
}, [])
const handleClick = () => {
    setCount(count+1)
}

return (
        <div onClick={handleClick}>
            click to add, count: {count}
        </div>
    )
    

我们期待在点击之后, 打印出count也能更新, 但是事实是, 每次打印出的count都没变.

这就涉及到闭包陷阱的问题了

什么是闭包陷阱

参考这篇文章

简单来说, 就是react hooks在渲染的时候维护了一个链表, 来记录useState和useEffect的位置和值, (这也是state不能使用if else的原因, 因为可能会导致链表中state useEffect的顺序错乱, 从而不能获取到正确的数值)

在每次state更新时, 链表从头开始重新渲染, 但是由于上面示例中useEffect没有依赖任何state, 所以只有在第一次渲染的时候才会触发, setCount渲染更新时, useEffect里面的回调函数并没有触发 因此里面的setInterval里面的count还是初始化时的值,
并没有获取到最新的. 这就是闭包陷阱

怎么解决

使用useRef可以解决闭包陷阱的问题, 为什么?

因为useRef 每次拿到的都是这个对象本身, 是同一个内存空间的数据, 所以可以获取到最新的值

同理, 我们如果这样浅拷贝, 也是可以获取到最新的值的

useEffect(() => {
        setInterval(() => {
            console.log(count)
        }, 1000)
        //闭包陷阱
    }, [])
    
const handleClick = () => {
        setCount((prevState) => {
            //浅拷贝
            return Object.assign(prevState, {
                name: 'ssdsd',
            })
        })
        setDate('yesterday')
    }
    
return (
        <div onClick={handleClick}>
            click to add, count: {count.a}, name:{count.name}
        </div>
    )

在点击后setInterval中的值也会更新, 因为本质上就是同一个内存空间的一个对象

或者这样, 也可以证明上述结论

const handleClick = () => {
        setCount((prevState) => {
            //浅拷贝
            let aaa = prevState
            aaa.name = 'sfss'
            return aaa
        })
        setDate('yesterday')
    }