• 作者:老汪软件技巧
  • 发表时间:2024-09-14 15:02
  • 浏览量:

1 创建项目

pnpm create vite react-closure-test --template react-ts

2 修改 index.tsx

3 示例组件

下面是一个通过定时器不断累加 count 的组件:

4 问题分析

大家可能会认为 count 会每秒加 1,但实际上不会。

原因是 setCount 时拿到的 count 一直是 0。

这是因为 useEffect 的依赖数组是空的 [],即只会执行并保留第一次的函数,而第一次的函数引用了当时的 count,形成了闭包。

5 解决方法5.1 方法一:使用 setState 的函数参数

可以通过 setState 的函数参数来解决闭包问题:

运行结果:

5.2 方法二:使用 useReducer

使用 useReducer 也可以解决闭包问题,因为它是通过 dispatch 一个 action,不直接引用 state:

5.3 方法三:使用 useEffect 的依赖数组

在某些情况下,必须使用 state,这时可以利用 useEffect 的依赖数组:

这种方法虽然能解决闭包陷阱,但在定时器的场景中并不合适,因为每次 count 变化都会重新启动定时器。

5.4 方法四:使用 useRef

可以使用 useRef 来解决定时器的闭包问题:

通过 useRef 创建 ref 对象,保存执行的函数,每次渲染更新 ref.current 的值为最新函数。

这样,定时器执行的函数里就始终引用的是最新的 count。

6 自定义 Hook

我们可以将定时器的处理封装成一个自定义 Hook:

使用 useCallback 包裹返回的函数,可以避免该参数的变化,配合 memo 可以减少不必要的渲染。

通过以上方法,我们可以有效地解决 React 中的闭包陷阱问题。