你觉得Hooks这一点烦吗?
源 / 文/ 卡颂
大家好,我卡颂。
昨天一个小伙伴发了一个Demo
给我,让我解释下原因。
我一看,好家伙,小小一个Demo
,知识点囊括了:
Hooks
的闭包问题state
是如何组装的
相信看完这个Demo
,对函数组件
会有更深的认识。
让人懵逼的Demo
Demo
包含一个按钮、一个列表。
<div className="App">
<button onClick={add}>Add</button>
{list.map(val => val)}
</div>
点击按钮,调用add
方法,向列表中插入一项:
let i = 0;
export default function App() {
const [list, setList] = useState([]);
const add = () => {
// ...
};
return (
<div className="App">
<button onClick={add}>Add</button>
{list.map(val => val)}
</div>
);
}
显示效果:
烧脑的地方在于,调用add
方法插入的是一个「点击后会调用 add 方法的按钮」:
const add = () => {
setList(
list.concat(
<button
key={i}
onClick={add}>
{i++}
</button>
)
);
};
点击Add
按钮7下后的显示效果:
那么问题来了,点击带数字按钮(会调用和点击Add按钮
一样的add
方法)后会有什么效果呢?
state的组装和闭包问题
如果你认为会插入一个新按钮:
那就错了。
正确答案是:点击对应按钮后list
长度变为「按钮对应数字 + 1」,且最后一项的数字为「点击前最大数字 + 1」。
比如,点击前最大数字为6
如果点击 0,list
长度变为0 + 1 = 1
,且最后一项为6 + 1 = 7
:
如果点击 2,list
长度变为2 + 1 = 3
,且最后一项为6 + 1 = 7
:
这是两个因素共同作用的结果:
Hooks
的闭包问题state
是如何组装的
原因分析
再来看看add
方法:
const add = () => {
setList(
list.concat(
<button
key={i}
onClick={add}>
{i++}
</button>
)
);
};
button
点击后调用add
,所以会基于add
所属上下文(App
函数)形成闭包,闭包中包括:
add
list
setList
i
属于module
级作用域,不在该闭包内
其中list
与setList
来自于useState
调用后的返回值:
const [list, setList] = useState([]);
一种常见的认知误区是:多次调用useState
返回的list
是同一个引用。
事实上,每次调用useState
返回的list
都是基于如下公式计算得出的:
基准state + update1 + update2 + ... = 当前state
所以是一个全新的对象。
如果你想了解更多
update
、state
计算的细节,参考React技术揭秘[1]
当首屏渲染
时:
App
组件首次render
创建
list = []
<button onClick={add}>Add</button>
依赖add
,形成闭包,闭包中的list = []
接下来,点击Add按钮
:
调用
add
方法,该方法来自于首屏渲染
创建的闭包add
方法中依赖的list
来自于同一个闭包,所以list = []
<button key={i} onClick={add}>{i++}</button>
依赖add
,形成闭包,闭包中的list = []
所以,对于按钮0
,
任何时候点击他实际上执行的都是:
setList(
[].concat(
<button key={i} onClick={add}>{i++}</button>
)
);
那么如何修复这个问题呢,也很简单,将setList
的参数改为函数形式:
// 之前
setList(list.concat(<button key={i} onClick={add}>{i++}</button>));
// 之后
setList(list => list.concat(<button key={i} onClick={add}>{i++}</button>));
函数参数中的list
来自于Hooks
中保存的list
,而不是闭包中的list
。
总结
由于Hooks
总是在组件render
时才会计算新状态,这为Hooks
带来比较重的心智负担。
相比而言,采用「细粒度更新」实现的Hooks
(比如VUE
的Composition API
)可以实时更新状态,操作起来更符合直觉。
在使用Hooks
过程中,你有没有遇到类似的头疼问题呢?
参考资料
React技术揭秘:https://react.iamkasong.com/state/mental.html#%E5%90%8C%E6%AD%A5%E6%9B%B4%E6%96%B0%E7%9A%84react
推荐阅读
中国唯一一座没有高楼大厦的新一线城市,也太佛了吧
国内有程序员电视剧了,结果看了一分钟,就吐了...
华为最美小姐姐被外派墨西哥后...
END
顶级程序员:topcoding
做最好的程序员社区:Java后端开发、Python、大数据、AI
一键三连「分享」、「点赞」和「在看」