前言
在刚开始使用react hooks时就遇到过这个问题,
当时是一个滑动加载分页的场景,
总是不能加载到最新页,
当时分析造成问题的原因,
1.EventLoop造成的,添加Promise、setTimout试图拿到最新值
2.闭包缓存老值拿不到最新值造成的,
于是把scoll函数从useEffect(()=>window.addEventListener("scroll",function scroll),[])
改成useEffect(()=>window.addEventListener("scroll",()=>scroll(ref.current),[])形式,问题得到解决
但猜想始终是猜想,问题解决也是根据猜想基于经验的不断尝试,并没有探究造成此问题的根本原因,今天拿出时间一探究竟
react hook闭包模拟重现
const obj = {
fn:(val)=>{
console.log(val);
}
}
let fn = null
function getVal(val){
if(val===1){
fn = ()=>obj.fn(val);
}
fn()
}
getVal(1);
getVal(2);
getVal(3);
分析
这https://juejin.cn/post/7093699777556119565、
https://juejin.cn/post/7093931163500150820两篇文章对react闭包的分析很不错,
但写的很繁琐,现在本人基于这两篇文章做一个概括性说明,
首先明确闭包是什么,
闭包就是函数的定义和执行不在同一个作用域,而函数内部变量的作用域是在函数定义时确定的,
再明确useEffect(function,deps)的执行规则是什么,
useEffect(function,deps)是在组件首次加载执行后,当组件更新渲染时会对比old deps和new deps,
当不同时function执行,相同function执行,
那这两规则作用在一起为什么会造成react的闭包呢?
组件更新时,因为old deps和new deps相同,function不会重新创建执行,更新时的function还是组件首次加载或上次加载时的函数,而function中的变量是在函数定义时确定的,所以取的还是老值
那为什么通过添加deps或函数传参时能解决问题呢?
1.添加deps,组件更新时,old deps和new deps不同,function重新创建执行,此时new function内变量的作用域就是更新后的函数组件的作用域
2.函数传参,既然函数内拿的是旧值,那就通过函数传参的形式把最新的值传递进去即可,通过ref保存最新值,传递到函数内部,甚至函数内部直接拿ref上的值也可