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

使用useReducer出现更新深度超限问题,useState则正常

问题描述

我有一个监听输入框内容的useEffect,一开始用useState实现时运行流畅,但改成useReducer后出现异常——即使用户没有输入内容,useEffect也会持续触发,而useState版本没有这个问题。

相关代码

useReducer版本的useEffect

useEffect(() => {
dispatch({type : 'username', payload : watch().username});
dispatch({type : 'password', payload : watch().password});
console.log(state.username, state.password);
}, [watch()]);

useState版本的useEffect

useEffect(() => {
setPassword(watch().password);
setUsername(watch().username);
console.log(username, password);
}, [watch()]);

reducer实现

const reducer = (state, action) => {
switch (action.type) {
  case "username":
    return {...state, username: action.payload };
  case "password":
    return {...state, password : action.payload};
}
};

问题原因

  1. 依赖项引用变化:watch()(大概率是react-hook-form这类表单库的方法)每次调用都会返回新的对象引用,哪怕表单值没变化,useEffect检测到依赖更新就会触发。
  2. useReducer的无意义更新:React的useState会自动跳过值未变化的状态更新,但你的reducer每次都返回新的{...state}对象——哪怕payload和当前状态值完全一致,React也会认为状态已更新,触发组件重渲染,进而再次执行useEffect,形成循环触发。

解决方案

1. 给reducer加值判断,避免无意义更新

在reducer里先判断payload是否和当前状态值一致,一致就返回原state,不生成新对象:

const reducer = (state, action) => {
switch (action.type) {
  case "username":
    if (state.username === action.payload) return state;
    return {...state, username: action.payload };
  case "password":
    if (state.password === action.payload) return state;
    return {...state, password : action.payload};
  default:
    return state; // 必须加default分支,防止返回undefined
}
};

2. 优化useEffect的依赖项

不要直接把watch()返回的整个对象当依赖,改成依赖具体的字段,或者用useMemo缓存结果:

// 方式一:依赖具体字段
const { username, password } = watch();
useEffect(() => {
dispatch({type : 'username', payload : username});
dispatch({type : 'password', payload : password});
console.log(state.username, state.password);
}, [username, password]);

// 方式二:用useMemo缓存watch结果
const formValues = useMemo(() => watch(), [watch]);
useEffect(() => {
dispatch({type : 'username', payload : formValues.username});
dispatch({type : 'password', payload : formValues.password});
console.log(state.username, state.password);
}, [formValues]);

3. 合并dispatch操作

没必要分两次dispatch,一次更新多个字段,减少状态更新次数:

const { username, password } = watch();
useEffect(() => {
dispatch({
  type: 'updateForm',
  payload: { username, password }
});
console.log(state.username, state.password);
}, [username, password]);

// 对应更新reducer
const reducer = (state, action) => {
switch (action.type) {
  case "updateForm":
    const { username, password } = action.payload;
    if (state.username === username && state.password === password) {
      return state;
    }
    return {...state, username, password };
  case "username":
    if (state.username === action.payload) return state;
    return {...state, username: action.payload };
  case "password":
    if (state.password === action.payload) return state;
    return {...state, password : action.payload};
  default:
    return state;
}
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 00:41:15