React useForm Hook动态设置嵌套键的useState值问题
实现React useForm Hook的嵌套键赋值功能
要支持类似address.Street这种嵌套键的赋值,核心是拆分路径字符串并逐层更新嵌套对象,同时遵守React状态不可变的原则。下面是具体的实现方案:
1. 编写嵌套值赋值工具函数
先实现一个工具函数setNestedValue,它接收原始对象、路径字符串和新值,返回更新后的新对象(不修改原对象):
function setNestedValue<T extends Record<string, any>>(obj: T, path: string, value: any): T { const keys = path.split('.'); return keys.reduce((acc, key, index) => { // 处理最后一个键,直接赋值 if (index === keys.length - 1) { return { ...acc, [key.toLowerCase()]: value }; // 对应示例中Street转street的需求,无需转小写可删除toLowerCase() } // 当前层级对象不存在则创建空对象,否则展开原对象 const currentValue = acc[key] || {}; return { ...acc, [key]: setNestedValue(currentValue, keys.slice(index + 1).join('.'), value) }; }, { ...obj }); }
2. 修改useForm的handleChange函数
替换原直接赋值逻辑为调用工具函数,同时调整key类型为string以支持嵌套路径:
import { useState, ChangeEvent } from 'react'; function useForm<T extends Record<string, any>>(initialData: T = {} as T) { const [data, setData] = useState<T>(initialData); const handleChange = <S extends unknown>(key: string, sanitizeFn?: (value: string) => S) => (e: ChangeEvent<HTMLInputElement & HTMLSelectElement>) => { let value: string | number | S = e.target.value; // 若传入 sanitize 函数,先处理输入值 if (sanitizeFn) { value = sanitizeFn(value); } // 用函数式更新获取最新状态,调用工具函数完成嵌套赋值 setData(prevData => setNestedValue(prevData, key, value)); }; return { data, handleChange }; }
3. 调用示例
现在可以直接传入嵌套路径作为key:
// 定义表单数据类型 type FormData = { userName: string; address: { street: string; }; }; function MyForm() { const { data, handleChange } = useForm<FormData>(); return ( <div> <input type="text" name="Username" value={data.userName || ''} placeholder="用户名" onChange={handleChange('userName')} required /> <input type="text" name="Street" value={data.address?.street || ''} placeholder="街道" onChange={handleChange('address.Street')} required /> </div> ); }
关键说明
- 工具函数通过
split('.')拆分路径,用reduce逐层遍历并创建新对象,保证原状态不被修改,符合React状态更新规范。 - 使用
setData(prevData => ...)的函数式更新,避免闭包导致的旧状态引用问题。
内容的提问来源于stack exchange,提问作者Simagdo
相关产品推荐
相关产品推荐

