React组件重挂载时依赖未变但useEffect仍触发问题排查
问题原因分析
- props.initialValues的引用变化:即使react-query命中缓存返回相同数据内容,若
fetchCaseById返回的是新对象引用(或封装的api.getCaseData存在深拷贝逻辑),React的useEffect依赖采用浅比较,会判定值发生变化,触发回调。 - 不必要的reset触发逻辑:第二个useEffect同时监听
props.initialValues和props.isRefetching,组件重挂载时,只要initialValues引用变化,即使isRefetching为初始的false,也会执行reset(props.initialValues),覆盖Context中存储的用户修改后的表单值。
修复方案
方案1:仅在refetch完成时触发重置
调整useEffect依赖为isRefetching,并通过对比前后状态,只在refetch从执行状态变为完成状态时重置表单:
// 先实现一个获取上一轮状态的钩子 function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }, [value]); return ref.current; } // 在CaseForm组件中使用 const prevIsRefetching = usePrevious(props.isRefetching); useEffect(() => { // 当isRefetching从true变为false,说明refetch完成,执行重置 if (prevIsRefetching && !props.isRefetching) { reset(props.initialValues); } }, [props.isRefetching, prevIsRefetching, reset, props.initialValues]);
方案2:用深比较判断是否需要重置
如果必须监听initialValues,通过深比较确认内容真的变化后再执行reset,避免引用变化导致的误触发:
// 可以用lodash的isEqual,或自己实现深比较函数 import _ from 'lodash'; useEffect(() => { if (!props.isRefetching) { const currentValues = getValues(); // 仅当内容确实不同时才重置 if (!_.isEqual(currentValues, props.initialValues)) { reset(props.initialValues); } } }, [props.initialValues, props.isRefetching, getValues, reset]);
方案3:优化表单初始化优先级
确保useForm优先使用Context存储的表单值,同时同步refetch后的新初始值:
const { caseFormValues, setCaseFormValues } = useCaseProvider(); const prevIsRefetching = usePrevious(props.isRefetching); // 挂载时初始化Context(仅当Context无值时) useEffect(() => { if (!caseFormValues) { setCaseFormValues(props.initialValues); } }, [caseFormValues, setCaseFormValues, props.initialValues]); // 卸载时保存当前表单值到Context useEffect(() => { return () => { setCaseFormValues(getValues()); }; }, [setCaseFormValues, getValues]); // 表单初始化优先取Context值 const { handleSubmit, control, reset, formState: { errors }, } = useForm({ defaultValues: caseFormValues || props.initialValues, }); // refetch完成时同步更新Context和表单 useEffect(() => { if (prevIsRefetching && !props.isRefetching) { setCaseFormValues(props.initialValues); reset(props.initialValues); } }, [props.isRefetching, prevIsRefetching, reset, props.initialValues, setCaseFormValues]);
关键注意点
- React useEffect的依赖是浅比较,对象/数组引用变化会被判定为值变化,需针对性处理。
- 利用Context存储表单状态时,要明确初始化和重置的优先级,避免外部props意外覆盖用户修改。
- 针对react-query的refetch操作,通过监听
isRefetching的状态变化触发逻辑,比依赖数据引用更可靠。
内容的提问来源于stack exchange,提问作者Leff
相关产品推荐
相关产品推荐

