React中如何正确更新包含嵌套对象的useState状态?
原有的handleChange仅支持更新details的顶级属性,当需要更新嵌套在contact、time下的子属性时,原逻辑会直接在details根节点新增同名属性,不会修改嵌套对象内的对应值,因此无法生效。
方案1:原生React实现(无额外依赖)
首先调整所有表单组件的name属性,用点分隔表示属性层级:
{/* 顶级属性保持原有命名即可 */} <TextField label="Description" onChange={handleChange} name="description" value={details.description} /> {/* 嵌套属性用 . 分隔层级 */} <TextField label="email" onChange={handleChange} name="contact.email" value={details.contact.email} /> <TextField label="活动开始时间" onChange={handleChange} name="time.start" value={details.time.start} />
重写handleChange,自动解析层级并更新状态:
function handleChange(evt) { const { name, value } = evt.target; const keyPath = name.split('.'); setDetails(prevState => { // 深拷贝原有状态,避免直接修改原状态 const newState = structuredClone(prevState); // 逐层定位到要修改的嵌套对象 let currentLevel = newState; for (let i = 0; i < keyPath.length - 1; i++) { currentLevel = currentLevel[keyPath[i]]; } // 赋值最终属性 currentLevel[keyPath.at(-1)] = value; return newState; }); }
如果需要兼容不支持structuredClone的旧环境,可以替换为JSON.parse(JSON.stringify(prevState))或者引入lodash的cloneDeep方法。
方案2:用Immer简化不可变更新
如果项目可以引入第三方依赖,推荐使用Immer来简化嵌套状态的更新逻辑,不用手动处理状态拷贝:
- 首先安装依赖:
npm install use-immer
- 将原有
useState替换为useImmer:
import { useImmer } from 'use-immer'; const [details, setDetails] = useImmer({ description: null, platform: "Xbox", region: "ASIA", time:{ start: "2017-05-24T10:30", end: "2017-05-28T10:30", }, contact: { email: "", phone: "", twitter: "", discord: "", } });
- 重写
handleChange:
function handleChange(evt) { const { name, value } = evt.target; const keyPath = name.split('.'); setDetails(draft => { let currentLevel = draft; for (let i = 0; i < keyPath.length - 1; i++) { currentLevel = currentLevel[keyPath[i]]; } currentLevel[keyPath.at(-1)] = value; }); }
Immer会自动基于你对draft的修改生成符合不可变原则的新状态,代码更简洁,也能避免手动拷贝状态时的失误。
内容的提问来源于stack exchange,提问作者Hussam Khatib
相关产品推荐
相关产品推荐

