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

如何让自定义Hook的状态更新函数兼容useState用法并自动处理数据?

兼容原生调用的useForm Hook状态更新实现

核心方案

要同时支持直接传值和函数式更新,并且在开启autoUpdate时自动为字段添加计算属性,核心是先统一获取原始更新状态,再对状态进行自动处理,最后调用原生useState的setter完成更新。

完整代码实现

import { useState, useCallback } from 'react';

// 定义输入字段类型:基础字段结构 + 自动计算的res属性
type InputField = {
  value: string | number;
  res?: number;
};

// 表单状态类型:键为字段名,值为InputField
type InputFieldsState = Record<string, InputField>;

// 支持两种更新方式:直接传状态对象,或接收prevState的更新函数
type SetInputFieldsAction = InputFieldsState | ((prevState: InputFieldsState) => InputFieldsState);

export function useForm(initialState: InputFieldsState, autoUpdate = true) {
  const [inputFields, setOriginalState] = useState<InputFieldsState>(initialState);

  const setInputFields = useCallback((action: SetInputFieldsAction) => {
    // 第一步:获取用户更新后的原始状态
    const rawUpdatedState = typeof action === 'function'
      ? (action as (prev: InputFieldsState) => InputFieldsState)(inputFields)
      : action;

    // 第二步:如果开启autoUpdate,为每个字段添加计算属性res
    const finalState = autoUpdate
      ? Object.fromEntries(
          Object.entries(rawUpdatedState).map(([fieldName, field]) => [
            fieldName,
            {
              ...field,
              // 示例计算逻辑:字符串取长度,数字乘2,可根据需求修改
              res: typeof field.value === 'string' ? field.value.length : field.value * 2
            }
          ])
        )
      : rawUpdatedState;

    // 第三步:调用原生setter更新状态
    setOriginalState(finalState);
  }, [inputFields, autoUpdate]);

  return { inputFields, setInputFields };
}

关键细节说明

  • 类型兼容:通过SetInputFieldsAction联合类型明确两种更新参数的类型,解决函数式更新时的类型错误。
  • 更新逻辑统一:不管用户用哪种方式更新,先拿到原始更新结果,再统一处理计算属性,保证autoUpdate逻辑在所有场景下生效。
  • 性能优化:用useCallback缓存setInputFields,避免组件每次重渲染都创建新函数,减少不必要的子组件渲染。

使用示例

import { useForm } from './useForm';

function DemoForm() {
  const { inputFields, setInputFields } = useForm(
    {
      username: { value: '' },
      score: { value: 0 }
    },
    true // 开启自动计算
  );

  // 直接传值更新
  const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInputFields({
      ...inputFields,
      username: { value: e.target.value }
    });
  };

  // 函数式更新(依赖prevState)
  const handleAddScore = () => {
    setInputFields(prev => ({
      ...prev,
      score: { value: prev.score.value + 10 }
    }));
  };

  return (
    <div>
      <input 
        type="text" 
        value={inputFields.username.value} 
        onChange={handleUsernameChange}
        placeholder="输入用户名"
      />
      <p>用户名长度:{inputFields.username.res}</p>

      <button onClick={handleAddScore}>加分</button>
      <p>双倍分数:{inputFields.score.res}</p>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者SlothOverlord

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 01:54:12