1、在 react 中,当父组件向子组件传递一个函数的时候,那个函数中包含一个变量,state ,会产生闭包问题
上代码:
// 父组件
const uploadTable = (props) => {
const {
uploadingFileList,
} = props;
const deleteFile = (file) => {
const fileList = uploadingFileList.filter(f => f.uid !== file.uid);
console.log(fileList); // []
}
return (
<UploadingTable
deleteFile={deleteFile}
uploadingFileList={uploadingFileList}
/>
)
}
当子组件 调用 父组件 deleteFile 函数的时候,会发现 里面 uploadingFileList 为空
这种情况下,自然执行不下去了
经过检查,这里发生了闭包的现象,也就是说,这里的 deleteFile 函数 uploadingFileList 发生了闭包
解决1: 自然是 纯纯地把 uploadingFileList 通过 deleteFile 再传回来,但是一点逼格都没有
解决2:上代码 重点在于 使用 useRef
// 父组件
const uploadTable = (props) => {
const {
uploadingFileList,
} = props;
const uploadingFileListRef = useRef('');
uploadingFileListRef.current = uploadingFileList;
const deleteFile = (file) => {
// 重点在于 使用 useRef
const fileList = uploadingFileListRef.current.filter(f => f.uid !== file.uid);
console.log(fileList); // [正确有数据]
}
return (
<UploadingTable
deleteFile={deleteFile}
uploadingFileList={uploadingFileList}
/>
)
}
这里 就圆满地解决了 这个问题。
毕竟即使是闭包,但是闭包里面引入的终究是一个 地址,而这个地址里面的 数据变化是可以被读取到的
2、事实上,当使用 useCallback 也一样会引起闭包的问题,值得注意
不过useCallback 的话,只需要在 监听的数组里放入对应的数据即可解决
3、但是当 useState 中,调用 setXXX 的时候,可以选择朝里面传一个函数进去,函数的 参数就是 当前的 state ,这样可以避免 闭包、异步等等一系列的问题,如下:
setCurStep(cur => cur + 1);
4、当然,还有一种特殊的方法能避免 很多的闭包问题
import React, { useRef, useEffect, useState } from 'react';
let page = 1; // 将 参数 提到 函数外面来
const App = (param) => {
return (
<div>
</div>
);
}
总结:
因为 产生闭包 就其更本,大多数 都是因为
state、props 改变了之后,会导致 当前的函数组件重新 执行
上一次 执行的 代码中,使用的就是 执行上下文中 保存的 之前的 值,而不是 现在 改变之后的结果
所以 使用 useRef 也好,将 变量拿到外面也好,都是 让函数 里面的变量是同一个
但是这样的话,变量的改变不会 让函数组件 重新执行,
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
因此,类似于上面这样的代码,你会发现 count 只有在 第一次执行之后,就不会再 有任何改变
为什么?
因为 这里 useEffect 设置了一个 空数组的 依赖,也就是说,这个 useEffect 只会执行一次,接下来这个 函数组件继续执行的时候, 这里返回的其实是 第一次执行的结果。
也就是说,这里 setInterval 也形成了闭包,一只包着 第一次执行的结果
useEffect(() => {
const id = setInterval(() => {
setCount(count => count + 1); // 改成一个函数
}, 1000);
return () => clearInterval(id);
}, []);
题外话:
记录一个很有意思的 问题
var a = 100;
let b = async () => {
a = a + await 10;
console.log('2', a);
}
b();
a ++;
console.log('1', a);
// 1 101
// 2 110
这里其实还因为 运算 过程的问题,先是 计算出了 这个表达式的一半,那就是 100,也就变成了 a = 100 + await 10
然后 计算 异步调用 之后,a = 100 + 10
var tmp = 100
async function test() {
tmp = await new Promise((res) => {
setTimeout(() => {
res(tmp + 10)
})
})
console.log(2, tmp)
}
test()
tmp++
console.log(1, tmp)
// 1 101
// 2 111