大家好,我卡颂。
昨天一个小伙伴发了一个 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 技术揭秘
当 首屏渲染
时:
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
过程中,你有没有遇到相似的头疼问题呢?
欢送退出人类高质量前端框架钻研群,带飞