React useEffect内变量赋值渲染丢失 子组件props更新异常排查
问题根因
两个独立问题导致异常:
- ESLint警告来源:你在useEffect外部的组件作用域声明了普通变量
fetchedRecipeData,组件每次重渲染都会重新执行函数体,这个变量的赋值会被重置,无法跨渲染保留值。 - 表单不更新来源:
RecipeForm子组件大概率把父组件传入的recipeData只作为useState的初始值做了一次赋值,useState的初始参数仅在组件首次挂载时生效,后续props更新不会自动同步到子组件的本地state,所以哪怕父组件传了最新值,子组件的表单还是读取第一次初始化的空值。
修复步骤
1. 修正useEffect变量作用域,消除ESLint警告
不要在组件顶层声明普通变量存储接口返回值,直接把变量声明移到useEffect内部即可,不需要用useRef存这个一次性的接口返回值:
// 父组件EditRecipeFormWrapper.jsx相关代码 const [recipeData, setRecipeData] = useState({}); // 初始值给空对象,避免undefined报错 useEffect(() => { // 把fetch逻辑抽成内部async函数,避免useEffect回调直接写async的问题 const loadRecipeData = async () => { const res = await fetch(`/api/your-recipe-path`); // 直接在当前作用域声明变量,不需要跨渲染保留 const fetchedRecipeData = await res.json(); setRecipeData(fetchedRecipeData); }; loadRecipeData(); }, []); // 固定接口路径的话依赖数组留空是合法的
2. 修复子组件props不同步问题
选择以下任意一种方案修复即可:
- 方案A:子组件需要本地暂存编辑状态的场景,加useEffect监听props变化同步本地表单state
const RecipeForm = ({ recipeData }) => { // 首次挂载用传入的初始值(此时是空对象) const [formData, setFormData] = useState(recipeData); // 监听props传入的recipeData变化,同步更新本地表单状态 useEffect(() => { // 加空值判断,避免初始空状态覆盖有效数据 if (recipeData?.id) { setFormData(recipeData); } }, [recipeData]); // 表单项全部绑定formData,注意加空值兜底避免undefined触发受控组件警告 return ( <form> <input value={formData.name || ''} onChange={e => setFormData(prev => ({...prev, name: e.target.value}))} /> {/* 其余表单项同理 */} </form> ) }
- 方案B:不需要本地暂存、状态完全由父组件管控的场景,直接用props做受控组件,不需要声明本地state
const RecipeForm = ({ recipeData, setRecipeData }) => { return ( <form> <input value={recipeData.name || ''} onChange={e => setRecipeData(prev => ({...prev, name: e.target.value}))} /> {/* 其余表单项同理 */} </form> ) }
校验标准
修复完成后确认以下表现符合预期:
- ESLint的
react-hooks/exhaustive-deps警告消失 - 接口返回数据后,父组件状态更新、props传递、子组件表单渲染全链路正常,表单正确展示最新的食谱数据
- 控制台无“受控组件变为非受控组件”类的警告
内容的提问来源于stack exchange,提问作者deadant88
相关产品推荐
相关产品推荐

