什么是闭包?
闭包是指在一个函数内部包裹函数,并且内部函数总能访问外部函数的变量。
function f1(){
var num = 10,
function f2(){
alert(num) // 10
}
}
特点:1:函数套函数;
2:内部函数可以直接使用外部函数的局部变量或参数;
3:变量或参数不会被垃圾回收机制回收 GC
缺点:常驻内存 会增大内存的使用量 使用不当会造成内存泄露。
闭包陷阱以及如何解决
在react中我们使用其提供的Hooks中的useState,useEffect,useCallback 时,可能会造成闭包陷阱,下面我们来看一下出现的情况以及如何解决
useState【异步陷阱】
const [num, setNum] = useState(0)
const jia = () => {
setNum(num + 1)
console.log(num);
}
return (
<div>
<h1>{num}</h1>
<button onClick={() => jia()}>点我+1</button>
</div>
)
问题:当我们点击button按钮时,会发现值在持续 +1 ,但是打印的值始终都是上一次点击的值,
因为useState修改值是异步更新,无法获取更新后的值
解决方案:
const [num, setNum] = useState(0)
const numRef = useRef()
const jia = () => {
setNum(num + 1)
setTimeout(() => { //模拟异步操作
console.log(numRef.current.innerText);
}, 0)
}
return (
<div>
<h1 ref={numRef}>{num}</h1>
<button onClick={() => jia()}>点我+1</button>
</div>
)
使用useRef来获取元素的值
useEffect【过期问题】
const [num, setNum] = useState(0)
useEffect(() => {
setInterval(() => {
setNum(num + 1)
}, 1000)
}, [])
return (
<div>
<h1>{num}</h1>
</div>
)
问题:useEffect是在页面加载时执行,如果第二个参数是一个空数组,则只执行一次,所以我们可以看到页面始终显示的都是1
解决方案:
const [num, setNum] = useState(0)
useEffect(() => {
let time = setInterval(() => {
setNum(num + 1)
clearInterval(time)
}, 1000)
}, [num])
return (
<div>
<h1>{num}</h1>
</div>
)
我们可以通过在空数组中传入依赖项,使得useEffect中的逻辑在num每次更改时都会执行,并在每次执行后都清楚掉定时器
useCallback【缓存问题】
useCallback是用来进行优化的函数,他的作用是返回一个记忆化的回调函数,该回调函数仅在其依赖项发生变化时才会更新。这在处理子组件的 props 或者优化事件处理函数等场景中非常有用。
我们在父组件向子组件传递数据,并在父组件修改值,我们会发现,子组件的useCallback中打印的一直都是0
解决方案:我们在useCallback的空数组中,传入我们的依赖项 props.num 这样每次props.num 发生改变就会重新执行代码
以上就是我们在使用React的Hooks中可以会遇到的一些闭包陷阱以及解决方案~