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

使用forwardRef自定义Input时react-hook-form的isDirty始终为false问题

问题解决:自定义forwardRef组件导致react-hook-form的isDirty/dirtyFields失效及类型错误

问题根源

react-hook-form的isDirty/dirtyFields依赖对比字段的默认值和当前值,且需要获取原生input的DOM ref来跟踪字段变化。当使用自定义forwardRef组件时,如果没有正确将ref传递到内部的原生input元素,表单无法检测到字段值的变更,从而导致isDirty始终为false、dirtyFields为空对象。你遇到的Cannot set properties of null (setting 'current')类型错误,通常是ref传递逻辑错误(比如未正确转发ref、类型定义错误)导致的。

解决方案

1. 正确实现带forwardRef的自定义Input组件

确保自定义组件将接收的ref直接传递给内部的原生input(包括styled-components包装的input),同时正确定义类型(TypeScript场景):

import React, { forwardRef } from 'react';
import styled from 'styled-components';

// 样式化原生input
const StyledInput = styled.input`
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
`;

// 用forwardRef转发ref到原生input元素
// 泛型参数:第一个是ref的类型(HTMLInputElement),第二个是组件props类型
const CustomInput = forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
  ({ placeholder, ...restProps }, ref) => {
    return <StyledInput {...restProps} ref={ref} placeholder={placeholder} />;
  }
);

export default CustomInput;

2. 在表单中正确使用自定义组件

有两种推荐方式:

方式一:直接配合register使用

将register返回的属性(包括ref)直接传递给自定义组件:

import { useForm } from 'react-hook-form';
import CustomInput from './CustomInput';

function UserForm() {
  const {
    register,
    handleSubmit,
    formState: { isDirty, dirtyFields, touchedFields }
  } = useForm({
    // 必须设置defaultValues,否则isDirty无法正常工作
    defaultValues: {
      username: '',
      email: ''
    }
  });

  const onSubmit = (formData) => {
    console.log('提交数据:', formData);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} style={{ gap: '12px', display: 'flex', flexDirection: 'column' }}>
      <CustomInput {...register('username')} placeholder="请输入用户名" />
      <CustomInput {...register('email')} placeholder="请输入邮箱" />
      <button type="submit">提交表单</button>
      
      <div>
        <p>isDirty: {isDirty ? '✅ 是' : '❌ 否'}</p>
        <p>dirtyFields: </p>
        <pre>{JSON.stringify(dirtyFields, null, 2)}</pre>
        <p>touchedFields: </p>
        <pre>{JSON.stringify(touchedFields, null, 2)}</pre>
      </div>
    </form>
  );
}

export default UserForm;

方式二:使用Controller组件包裹(适合复杂自定义组件)

如果自定义组件包含额外逻辑(比如状态管理、格式转换),推荐用Controller来封装,它会自动处理ref和值的同步:

import { useForm, Controller } from 'react-hook-form';
import CustomInput from './CustomInput';

function UserForm() {
  const {
    control,
    handleSubmit,
    formState: { isDirty, dirtyFields, touchedFields }
  } = useForm({
    defaultValues: {
      username: '',
      email: ''
    }
  });

  const onSubmit = (formData) => {
    console.log('提交数据:', formData);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} style={{ gap: '12px', display: 'flex', flexDirection: 'column' }}>
      <Controller
        name="username"
        control={control}
        render={({ field }) => (
          <CustomInput {...field} placeholder="请输入用户名" />
        )}
      />
      <Controller
        name="email"
        control={control}
        render={({ field }) => (
          <CustomInput {...field} placeholder="请输入邮箱" />
        )}
      />
      <button type="submit">提交表单</button>
      
      <div>
        <p>isDirty: {isDirty ? '✅ 是' : '❌ 否'}</p>
        <p>dirtyFields: </p>
        <pre>{JSON.stringify(dirtyFields, null, 2)}</pre>
        <p>touchedFields: </p>
        <pre>{JSON.stringify(touchedFields, null, 2)}</pre>
      </div>
    </form>
  );
}

export default UserForm;

3. 修复"Cannot set properties of null"类型错误

这个错误通常是以下原因导致:

  • 自定义组件未使用forwardRef,导致react-hook-form无法获取到原生input的ref
  • 类型定义错误:未正确指定ref的类型为HTMLInputElement,导致ref被初始化为null且无法赋值
  • 未设置defaultValues:react-hook-form需要默认值来对比判断字段是否变更

按照上述步骤实现后,该错误会自动解决。

关键注意事项

  • 必须设置defaultValues:isDirty/dirtyFields是基于defaultValues和当前字段值的对比结果,如果未设置defaultValues,表单无法判断字段是否变更
  • 不要手动修改react-hook-form传递的ref:让register或Controller自动管理ref,避免手动赋值导致的null错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 18:46:13