You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:40:19