React 中闭包陷阱问题分析

背景

我在现在这家公司,一直都是用 React 做开发的,对 React 的闭包陷阱也有一定的了解,但是要我解释为什么会有闭包陷阱这个问题,也知道解决闭包的方式,就是 setState 的时候,传一个函数就能解决,但是原理和为什么是这样却说不清楚。记得在一次面试中,面试官还问过我这个问题,当时也是支支吾吾,说不清楚。后来在搜索引擎上搜索 React 闭包陷阱相关的资料,出现的例子的 setIntervalsetTimeout,但是我日常开发中,很少使用这些东西,感觉很难理解。我一直记着这个问题,有机会就会回头再看看,最后看到一个比较好理解的例子,所以就做一次学习记录。

代码

代码还是很简单的,就是有一个全局变量 i, 还有一个按钮,每次点击按钮,我们就往数组里头放一个按钮,按钮的上的文字就是 全局变量 i + 1i 只是用于记录文字,和闭包陷阱没有关系。

import { useState } from "react";
import type { ReactElement } from 'react';

let i = 0;

const DemoBtn = () => {
  const [list, setList] = useState<ReactElement[]>([])const add = () => {
    setList(list.concat(<button key={i} onClick={add}>{i++}</button>));
  };

  return (
    <div className="App">
      <button onClick={add}>Add</button>
      {list.map((val) => val)}
    </div>
  );
};

export default function App() {
  return <DemoBtn />;
}

UI 显示就如下图:
在这里插入图片描述

在 sandbox 中试试吧

问题出现

一些看起来都是那么的正常,我点击 Add 按钮,他就自然的往后加按钮。但是如果你不小心,点了 1 ~ 7 中的其他按钮,比如我点击了 2 。结果
在这里插入图片描述
这。。。。是什么情况?你点击的数字会让数组的长度变成对应的数字。比如我点击了 2,数组的长度就变成了 2。

调试

我们不妨打上断点调试一下,我的调试结果是这样的,我点击的是 2 的按钮,结果就如图所示:
在这里插入图片描述
在这个闭包中,list 数组只有两个元素,如果你尝试别的数字,也是一样的。

分析

listsetList 是我们调用 useState() 返回的,我之前还以为,每次调用 useState 返回的是同一个 list,或者说没有想过,会是不同的数据。但是实际上 useState 返回的 list 是基于 base state 计算出来的

current state = base state + update1 + update2 + …

每次会将上一次的 stateupdate 进行合并得到新的 current state
拿上面的例子来说,画个图来表示他的闭包,应该是这样子的
在这里插入图片描述

解决方法

相信解决方法大家都知道,也很简单,把原来 add 中的 setList 方法改成下面这样就可以啦

 const add = () => {
    setList((btnList) => btnList.concat(<button key={i} onClick={add}>{i++}</button>));
  };

在 sandbox 中试试吧

总结

useState 返回的 list 是基于 base state 计算出来的,并且由于闭包的存在,每个「数字按钮」add 函数中的 list 都不同

有个问题得说明一下,这并不是 React 框架的问题,这是 JavaScript 闭包的特点,而 useState 是基于 base state 更新的,所以就会显示出一种和我们预期不太一样的结果。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值