在组件回调函数获取最新值失败的原因和方案
总结:
1、原因:在 columns validate 回调函数中使用 组件变量时,变量永远是 生成 的值,后续的变更不会传入变化
- 函数组件每次渲染都是独立的闭包,所有的内部函数捕获的都是 创建时的状态快照,不是调用时的状态
2、解决方案:动态获取可以①使用 ref + ②useMemo
- useEffect + setColumns 容易产生闭包问题(示例如下),易遗漏 -> useMemo 更可靠
更新示例:在 columns onOper 时又遇到了值无法及时更新的情况
-
即使 onOper 虽然是最新的,但是函数本身可能持有旧数据 -> 若 getColumns 依赖的函数(如 onOper)未用 useCallback 缓存,即使 source 更新,validate 仍可能持有旧函数
-
onOper 使用 useEffect 监听没有生效 -> 可能是 useEffect 捕获的函数 getColumns 不是最新的
- 最后采用了③劫持回调,直接在 onOper 回调参数中抛出 data 数据
问题和原因
const [source, setSource] = useState([])
const [columns, setColumns] = useState([])
const getColumns = () => ([
{
title: '名称',
edit: true,
dataIndex: 'name',
validate: (_, record) => {
return source.name // ❌ 无法获取到最新值 -> 永远指向初始的 source(闭包问题)
}
}
])
useEffect(() => {
setColumns(getColumns())
}, []) // 只在挂载时执行一次
问题1、执行 getColumns 时,在 validate 中的回调函数中,无法获取当前组件 source 的最新值 -> 为什么?
-
原因:【闭包 + React Hooks 渲染机制】validate 是作为一个函数属性被写死在 columns 里的 -> ,getColumns 在组件首次渲染执行时,validate 捕获的是 当时的 source,即 闭包变量
-
知识点:react 的 hooks 闭包特性 - 函数组件每次渲染时,所有函数、变量、props、state 都是快照,案例中的 validate 引用的 source 其实是 生成 columns 那一刻的 source,而不是后续最新的
const getColumns = (s) => ([
{
validate: ((data) => {
// 依旧获取不到 source 的最新值
})(source),
}
])
问题2、给 getColumns 传递参数,使用立即执行函数,为什么不行?
- 原因:validate 只是在 生成时 闭包捕获了这个参数,它永远不会自动变成最新的 source,只会用 生成时捕获的那个值
解决方案
方案1、副作用
useEffect(() => {
setColumns(getColumns(source)) // 每次 source 变化时重新生成 columns
}, [source]) // 依赖 source
方案2、useMemo【推荐】
const columns = useMemo(() => getColumns(), [source]); // 自动依赖 source
方案3、使用 ref
validate: (_, record) => {
const data = sourceRef.current
return data.name
}