You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 00:35:31