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

使用react-hook-form的watch方法导致无限循环问题求助

React Hook Form 结合 useState 引发无限循环问题解决

问题场景

在Form组件中使用react-hook-form,需实时更新父组件App的状态,且Form组件无提交按钮,因此采用watch方法监听表单数据。App组件通过useState保存数据,尝试通过回调prop更新App数据时出现无限循环。

App组件代码

import { FC, useState } from 'react';

import { Form } from './Form';
export const App: FC<{ name: string }> = ({ name }) => {
  const [data, setData] = useState({ name: 'David' });
  return (
    <div>
      <h1>Hello {data.name}!</h1>
      <Form setData={setData} />
    </div>
  );
};

Form组件代码

import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';

export const Form = ({ setData }) => {
  const { register, watch } = useForm();
  const formData = watch();
  useEffect(() => {
    setData(formData);
  }, [formData]);

  return (
    <div>
      <h2>Form comp</h2>
      <form>
        <input name="name" {...register('name')} />
      </form>
    </div>
  );
};

问题根源

无限循环的核心逻辑链:

  1. watch()在每次组件渲染时都会返回新的对象引用,即便表单数据未发生变化
  2. 新对象触发useEffect的依赖项变更,调用setData更新App的state
  3. App更新state会触发自身及子组件Form重新渲染,watch()再次返回新对象,循环往复

解决方案

方案1:精准监听单个字段

若只需监听特定字段(如示例中的name),避免监听整个表单对象,直接指定字段监听:

import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';

export const Form = ({ setData }) => {
  const { register, watch } = useForm();
  // 仅监听name字段,返回值为字符串而非对象,引用稳定
  const name = watch('name');

  useEffect(() => {
    setData({ name });
  }, [name, setData]);

  return (
    <div>
      <h2>Form comp</h2>
      <form>
        <input name="name" {...register('name')} />
      </form>
    </div>
  );
};

方案2:使用useWatch保证引用稳定性

react-hook-form提供useWatch钩子,它会返回稳定的引用,仅当监听的字段实际变化时才更新:

import React, { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';

export const Form = ({ setData }) => {
  const { register, control } = useForm();
  // 使用useWatch替代watch,传入control控制引用稳定性
  const formData = useWatch({ control });

  useEffect(() => {
    setData(formData);
  }, [formData, setData]);

  return (
    <div>
      <h2>Form comp</h2>
      <form>
        <input name="name" {...register('name')} />
      </form>
    </div>
  );
};

方案3:手动添加数据浅比较

若必须监听整个表单,可在useEffect中手动对比前后数据,仅当数据真的变化时才更新父组件state:

import React, { useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form';

export const Form = ({ setData }) => {
  const { register, watch } = useForm();
  const formData = watch();
  // 用ref保存上一次的表单数据
  const prevDataRef = useRef(formData);

  useEffect(() => {
    // 浅比较对象内容是否变化(简单场景可用JSON.stringify,复杂场景建议用lodash.isEqual)
    if (JSON.stringify(prevDataRef.current) !== JSON.stringify(formData)) {
      setData(formData);
      prevDataRef.current = formData;
    }
  }, [formData, setData]);

  return (
    <div>
      <h2>Form comp</h2>
      <form>
        <input name="name" {...register('name')} />
      </form>
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:07:46