React表单页面重载时useState初始化表单数据丢失问题
问题根源
这是React useState 初始化机制的典型误用:
useState接收的初始参数仅在组件首次挂载时生效一次,后续组件重渲染不会重新计算初始值- 从列表页跳转进入表单时,全局Context中的
declas列表已经完成接口请求,组件初始化执行declas?.find(...)可以直接拿到对应申报数据,因此所有表单state初始化正常,页面展示无问题 - 刷新页面/直接通过链接访问时:
- 组件首次挂载阶段,Context内的
declas还未完成请求为空,declas?.find(...)返回undefined,所有表单state被初始化为空值/默认值 - 被注释的useEffect即使后续请求到了完整申报数据,也仅调用了
setDecla更新decla这一个state,其余event/owner/content等表单字段对应的state没有同步更新,自然无法展示数据 - 代码中
react-select组件使用的defaultValue属性同样仅在组件首次挂载时生效,后续对应state更新也不会触发组件重新渲染选中值,进一步导致数据不显示。
- 组件首次挂载阶段,Context内的
修复步骤
1. 完善数据请求逻辑
放开被注释的useEffect,拿到接口返回的申报数据后,同步更新所有表单关联的state,不要仅更新decla:
useEffect(() => { const fetchData = async () => { await GET(); const resData = await GET_decla(id); if (!resData) return; setDecla(resData); // 同步给所有表单state赋值 setEvent(resData.event); setOwner(resData.owner ?? user.lid_id); setContent(resData.content ?? ""); setTotal(resData.total ?? 0); setReceipt(resData.receipt); setBoekstuk(resData.boekstuk ?? ""); setContent_ficus(resData.content_ficus ?? ""); setDefaultValues( resData.present?.map(pres => optionsLeden?.find(opt => opt.value === pres)) || [] ); }; fetchData(); // eslint-disable-next-line }, [id]);
2. 修正react-select的受控属性
将所有react-select/CreatableSelect组件的defaultValue属性替换为value,保证state更新时组件能同步渲染最新值:
// 成员选择器 <Select value={optionsLeden?.find(x => x.value === owner)} options={optionsLeden?.filter(x => !["all", 19900].includes(x.value))} name="owner" id="id_present" onChange={e => setOwner(e.value)} /> // 活动选择器 <Select value={optionsEvents?.find(x => x.value === event?.id)} options={optionsEvents} name="event" onChange={e => setEvent(e.value)} /> // 记账科目选择器 <CreatableSelect value={optionsBoekstuk?.find(x => x.value === boekstuk)} options={optionsBoekstuk} name="boekstuk" id="id_boekstuk" onCreateOption={createBookstuk} onChange={e => setBoekstuk(e.value)} />
优化建议
当前表单拆分了8个独立的useState,后续维护很容易出现更新遗漏的问题,建议要么用单个useState统一管理整个表单对象,要么直接使用react-hook-form这类专门的表单库,降低状态同步的心智负担。
内容的提问来源于stack exchange,提问作者gmv
相关产品推荐
相关产品推荐

