参考文章
1. 从react hooks“闭包陷阱”切入,浅谈react hooks
Hooks
Hooks 是 react 自 16.8 引入的新特性,使得开发者在摆脱 class 定义组件的同时,也能够进行状态管理。这样,react 组件完全进入函数式(FP)编程范式。既然进入了函数式编程的范畴,那么闭包(closure)就成了不可回避的话题,所以,react hooks 陷阱,说白了其实就是闭包特性。
Hooks 陷阱
从实例开始:
function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setTimeout(() => { // 这个 setTimeout 会引起闭包陷阱 setCount(count + 1); }, 1000); }; const handleReset = () => { setCount(0); }; return ( <div> <p>Count: {count}</p> <button onClick={handleClick}>Increment</button> <button onClick={handleReset}>Reset</button> </div> ); }
在上面的代码中, handleClick 的调用中有一个异步的执行,这就开启了一个宏任务,所以,这就是闭包陷阱产生的地方。相似的场景:
for (var i=0; i<5; i++) { setTimeout(()=>{ console.log(i) }, 0) }
这是一段很经典的面试代码,如果按照上述的执行,其结果最终会打印 5 个 5 出来,而不是 0~4,其原因一样,异步任务产生的宏任务落后于主任务栈,那么该如何修改才能达到我们想要的效果?这里有两种思路:
// 一、将 var 改成 let,保证执行上下文的正确性 for (let i=0; i<5; i++) { ... }
// 二、利用闭包特性 for (var i=0; i<5; i++ ) { (function(i){ setTimeout(()=>{ console.log(i) }, 0) })(i); }
而我们的 Hooks 闭包陷阱就是利用第二种方式解决的。
解决方案
先对比下有问题的写法和解决方案的写法:
// 问题写法 const handleClick = () => { setTimeout(() => { // 这个 setTimeout 会引起闭包陷阱 setCount(count + 1); }, 1000); };
// 正确写法 setTimeout(() => { setCount(currentCount => currentCount + 1); }, 1000);
两者不同的地方是:错误写法中最终 setCount 的参数是一个具体的数值,而正确写法中传递的则是一个函数(这里基本上可以看出 react 对 FP 的编程范式的偏爱),以确保组件拿到的是最新的值。当然,这和 react 的系统有密切的关联,详情可以翻阅 react 的源码。
以上是对于 useState 这个 Hook 所产生的问题的 fix,那么对于 useEffect 也可能产生同样的问题,不过其解决方案更简单,只需要按照 react 的文档,注意补完 useEffect 的第二个参数即可
标签:闭包,react,Hooks,setCount,React,Hook,陷阱,setTimeout From: https://www.cnblogs.com/cc-freiheit/p/17589950.html