React中如何使用useState钩子更新数组中的嵌套对象?
问题分析与解决方案
错误原因
你当前的更新代码存在结构错误:在修改form_values[0]时,错误地展开了整个prevStyle.form_values数组(而非数组内的第一个对象),导致form_values被替换成不符合原结构的对象,后续读取form_values[0]自然会报错。
修复单个字段的更新(临时方案)
将更新代码里的...prevStyle.form_values改成...prevStyle.form_values[0],即可正确修改第一个表单项的field_seq:
<Form.Control type="number" value={documentObj.form_values[0].field_seq} onChange={(event) => { setDocument((prevState) => ({ ...prevState, form_values: [ { ...prevState.form_values[0], field_seq: parseInt(event.target.value, 10), // 转成数字类型,匹配初始的null类型 }, ], })); }} />
通用状态更新函数(最终方案)
要处理所有嵌套层级的输入更新,包括顶层字段、form_values数组项、甚至select_values里的子项,可以实现通用的不可变更新逻辑:
1. 通用不可变更新工具函数
这个函数能根据传入的字段路径(比如['form_values', 0, 'field_name']),在不修改原状态的前提下更新指定字段:
const updateNestedState = (state, path, value) => { const [key, ...restPath] = path; if (restPath.length === 0) { // 到达最后一层,直接更新值 if (Array.isArray(state)) { const newArray = [...state]; newArray[key] = value; return newArray; } return { ...state, [key]: value }; } // 递归处理嵌套层级 if (Array.isArray(state)) { const newArray = [...state]; newArray[key] = updateNestedState(newArray[key], restPath, value); return newArray; } return { ...state, [key]: updateNestedState(state[key], restPath, value), }; };
2. 通用输入变更处理器
基于上面的工具函数,实现一个通用的处理器,可通过参数指定要更新的字段路径:
const handleInputChange = (path) => (event) => { let { value } = event.target; // 根据字段类型做类型转换,匹配初始状态的类型定义 if (path.includes('field_seq')) { value = value === '' ? null : parseInt(value, 10); } else if (path.includes('is_mandatory') || path.includes('value')) { value = value === 'true'; } setDocument(prevState => updateNestedState(prevState, path, value)); };
3. 使用通用处理器的示例
现在不管是顶层字段还是嵌套字段,都可以用这个函数统一处理:
{/* 更新顶层的document_name */} <Form.Control type="text" value={documentObj.document_name} onChange={handleInputChange(['document_name'])} /> {/* 更新form_values[0]的field_seq */} <Form.Control type="number" value={documentObj.form_values[0].field_seq ?? ''} // 处理null值,避免输入框显示null onChange={handleInputChange(['form_values', 0, 'field_seq'])} /> {/* 更新form_values[0]的is_mandatory(复选框示例) */} <Form.Check type="checkbox" checked={documentObj.form_values[0].is_mandatory} onChange={(e) => handleInputChange(['form_values', 0, 'is_mandatory'])({ target: { value: e.target.checked } })} /> {/* 更新form_values[0].select_values[0]的label */} <Form.Control type="text" value={documentObj.form_values[0].select_values[0].label} onChange={handleInputChange(['form_values', 0, 'select_values', 0, 'label'])} />
补充说明
- 类型转换逻辑可根据你的实际字段需求调整,确保状态类型与初始定义一致;
- 如果
form_values是动态生成的数组(不止一个项),遍历数组时可将index作为路径参数传入,比如['form_values', index, 'field_name']。
内容的提问来源于stack exchange,提问作者alimukhtor
相关产品推荐
相关产品推荐

