React自定义FormContext中valueChange回调获取values为空问题求助
问题根源:闭包陷阱导致的旧状态引用
你遇到的问题本质是React的闭包特性:当你在组件的useEffect里调用onValueChange(myCallback)时,myCallback内部的values()函数捕获的是当时的controls状态——也就是初始的空对象。后续buildForm更新了controls,但存在Context状态里的callback还是旧的版本,它引用的controls永远是最初的空值,所以调用values()自然返回空对象。
两种修复方案
方案1:触发回调时主动传入最新表单值
直接在Context内部计算最新值并传给回调,彻底避开闭包问题:
// Context 内部代码 const [controls, setControls] = useState<GenericObject<FormControl>>({}); // 调整回调类型,让它接收最新值作为参数 const [callback, setCallback] = useState<((values: GenericObject) => void) | null>(null); useEffect(() => { if (!callback) return; // 计算最新的表单值并传入回调 const currentValues = Object.entries(controls).reduce((acc, [key, control]) => { acc[key] = control.value; return acc; }, {} as GenericObject); callback(currentValues); }, [controls, callback]); // 新增callback到依赖,避免旧回调残留 const onValueChange = (_callback: (values: GenericObject) => void) => { setCallback(_callback); }; // 保留原values方法供其他场景调用 const values = (): GenericObject => { return Object.entries(controls).reduce((acc, [key, control]) => { acc[key] = control.value; return acc; }, {} as GenericObject); };
组件中修改回调接收参数:
const myCallback = (currentValues) => { console.log(currentValues); // 这里拿到的就是最新表单值 }
方案2:用useRef保存最新的controls
如果不想修改回调参数结构,可以用useRef存储实时更新的controls,让values()从ref中取值:
// Context 内部代码 const [controls, setControls] = useState<GenericObject<FormControl>>({}); const controlsRef = useRef<GenericObject<FormControl>>({}); const [callback, setCallback] = useState<(() => void) | null>(null); // 每次controls更新时同步到ref useEffect(() => { controlsRef.current = controls; }, [controls]); useEffect(() => { if (!callback) return; callback(); }, [controls, callback]); // 新增callback到依赖 const values = (): GenericObject => { // 从ref中获取最新的controls return Object.entries(controlsRef.current).reduce((acc, [key, control]) => { acc[key] = control.value; return acc; }, {} as GenericObject); }; // buildForm 和 onValueChange 保持不变
这样不管回调什么时候触发,values()都会拿到最新的控件状态。
内容的提问来源于stack exchange,提问作者carloberd
相关产品推荐
相关产品推荐

