React hooks的闭包陷阱

80 阅读1分钟

什么是闭包陷阱?

在React的uesEffect中使用定时器,在定时器中对state进行操作,但是在useEffect的第二个参数中没有加入使用的state,就会形成闭包陷阱。 解释:从“闭包陷阱”这个词就能知道其中含义,在使用闭包的情况下,和我们预期的结果不太一样,就像一个陷阱。

闭包陷阱的案例

import React, {useEffect, useState} from "react";

export default function App() {
    const [count, setCount] = useState(0);
    useEffect(() => {
        setInterval(() => {
            setCount(count + 1);
        }, 5000)
        setInterval(() => {
            console.log(count)
        }, 5000)
    }, [])

    return (<>
        </>
    );
}

如果我们想要的结果是每隔5秒,打印的情况是 0 ,1,2,3,... 但是实际的情况是 0,0,0,0,....

原因

在useEffect的defs是[]的时候,钩子里面的callBack函数只执行一次,每一个钩子都有自己的上下文,这个时候虽然通过useInterval改变了state,但是钩子里面的上下文中的state并没有改变,一直使用的是第一次的state,所以就出现这种情况。

解决方案

defs的依赖添加

count每次改变,因为useEffect添加了依赖,所以useEffect的中引用的count也会改变,需要注意的是每次在创建定时器前,先要清理之前的定时器

import React, {useEffect, useState} from "react";

export default function App() {
    const [count, setCount] = useState(0);
    useEffect(() => {
        const timer1 = setInterval(() => {
            setCount(count + 1);
        }, 5000)
        const timer2 = setInterval(() => {
            console.log(count)
        }, 5000)
        return () => {
            window.clearInterval(timer1)
            window.clearInterval(timer2)
        }
    }, [count])

    return (<>
        </>);
}

使用useRef

useRef的特别之处是可以记录多次渲染的状态,或者说useRef只会在初次渲染的时候被创建

import React, {useEffect,useRef} from "react";

export default function App() {
    const ref = useRef(0)
    useEffect(() => {
        const timer1 = setInterval(() => {
            ref.current  +=1;
            console.log(ref.current)
        }, 5000)
    }, [])
    return (<>
        </>);
}