关于useEffect中didCancel与清理函数执行逻辑的技术问询
关于React useEffect清理函数的疑问
相关代码
useEffect(() => { let didCancel = false; async function fetchJoke() { if (!didCancel) { let response = await fetch('https//api.chucknorris.io/jokes/random'); let data = await response.json(); setJoke(data); } } fetchJoke(); // 清理函数 return () => { didCancel = true; }; }, [update]);
清理函数会在组件卸载或effect重新执行时被调用。
问题
我难以理解清理函数设置didCancel = true后,如何决定fetchJoke是否执行异步操作。每次组件卸载或effect重新执行时,let didCancel = false是否会运行?存在两处修改didCancel值的地方:
let didCancel = false- 清理函数
return () => { didCancel = true; };
请问哪一处的修改会生效,原因是什么?
解答
直接给你捋清楚整个逻辑:
每次effect执行,都会生成一套全新的局部变量和函数
当组件首次渲染,或者依赖项update变化时,useEffect的回调会重新跑一遍,这时候let didCancel = false会重新声明并赋值,同时重新定义fetchJoke函数,也会返回一个新的清理函数。清理函数和当前effect的局部变量是绑定死的
这是JavaScript闭包的特性:每一次effect产生的清理函数,只会“认”自己这次effect里的didCancel变量。举个具体例子:- 第一次执行effect:创建
didCancelA = false,定义fetchJokeA(判断的是!didCancelA),返回清理函数cleanupA(负责把didCancelA改成true),然后调用fetchJokeA。 - 这时候如果
update变了,React会先调用上一次的cleanupA,把didCancelA设为true;接着执行新的effect,创建didCancelB = false,定义fetchJokeB,返回cleanupB,调用fetchJokeB。
- 第一次执行effect:创建
不存在“哪一处生效”的冲突,因为对应不同批次的流程
比如第一次的fetchJokeA在await请求的时候,触发了effect重新执行:- 先跑
cleanupA把didCancelA改成true; - 等请求返回后,
fetchJokeA里的if (!didCancelA)就不成立,不会执行setJoke(data),避免给已经过时的组件状态赋值。 - 而新的
fetchJokeB对应的didCancelB是false,它的异步操作会正常走下去(除非又触发了新的effect)。
- 先跑
简单总结:每一轮effect的didCancel、fetchJoke、清理函数是一套绑定的,清理函数只会修改自己这套里的didCancel,fetchJoke也只会判断自己绑定的那个didCancel,两者互不干扰,各自对应自己的effect执行批次。
内容的提问来源于stack exchange,提问作者q0987
相关产品推荐
相关产品推荐

