stale-closure-in-react-hooks

由于闭包问题,开发者在某些作用域中得到了过时的state。

参考

创造stale closure:

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

export default function StaleClosure() {

    let [count, setCount] = useState(null)
    const countRef = useRef(null)

    console.log(`re-render?`)
    useEffect(() => {
        const intervalId = setInterval(() => {

            // console.log(`countRef.current`, countRef.current)
            setCount(count + 1)
            // setCount(count++)
            // setCount(state => state + 1)
            
            // countRef.current = countRef.current + 1
            // setCount(countRef.current)
        }, 500)
        return () => clearInterval(intervalId)
    }, [])
    return <span style={{ fontSize: 50 }}>{count}</span>
}
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • setInterval的传参函数中,由于使用了StaleClosure函数作用域的count变量,形成闭包,而且是 stale closure
    • setCount调用时,如果是count+1这样的一个值,相当于每个tick都是调用setCount(1)
    • 此时,组件StaleClosure不会re-render。
  • 不过代码中注释的三种写法是可以让组件在每个tick中re-render的。主要是两种思路
    • 用方法的时候,传入一个函数
    • 用useRef.current暂存。
  • 也是由于这个闭包stale closure的存在,想要在setInterval的传参函数加入拦截的时候,访问count变量不是一个好操作。
useEffect(() => {
    const intervalId = setInterval(() => {

        // if (count >= 10) return  //无法拦截,count永远是初始值
        if (countRef.current >= 10) return   //可以拦截
        console.log(`comparison`, count, countRef.current)
        // setCount(count + 1)
        // setCount(count++)
        setCount(state => state + 1)
        countRef.current = countRef.current + 1
        // setCount(countRef.current)
    }, 500)
    return () => clearInterval(intervalId)
}, [])
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14

待定

晓露寝安浅云逍遥十漾轻拟