从简单到复杂:何时用 useState,何时用 useReducer?

在 React 开发中,状态管理是驱动组件交互和数据流的核心。随着项目复杂度的提升,开发者常常面临一个看似简单却至关重要的抉择:我该使用 useState,还是 useReducer

同样是一个 注册表单 (字段:usernameemailpasswordconfirmPasswordacceptTerms),用 useStateuseReducer 各写一遍,方便直观对比。

useState 版本

jsx 复制代码
import { useState } from 'react';

function RegisterFormUseState() {
  const [username, setUsername] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [acceptTerms, setAcceptTerms] = useState(false);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (password !== confirmPassword) {
      alert('两次密码不一致!');
      return;
    }
    console.log({ username, email, password, acceptTerms });
    resetForm();
  };

  const resetForm = () => {
    setUsername('');
    setEmail('');
    setPassword('');
    setConfirmPassword('');
    setAcceptTerms(false);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={username} onChange={(e) => setUsername(e.target.value)} placeholder="用户名" />
      <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="邮箱" />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="密码" />
      <input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} placeholder="确认密码" />
      <label>
        <input type="checkbox" checked={acceptTerms} onChange={(e) => setAcceptTerms(e.target.checked)} />
        同意条款
      </label>
      <button type="submit">注册</button>
      <button type="button" onClick={resetForm}>重置</button>
    </form>
  );
}

特点

  • 每个字段一个 useState
  • 重置表单需要逐个清空。
  • 字段多了会有很多重复 setter。

useReducer 版本

jsx 复制代码
import { useReducer } from 'react';

const initialState = {
  username: '',
  email: '',
  password: '',
  confirmPassword: '',
  acceptTerms: false,
};

function reducer(state, action) {
  switch (action.type) {
    case 'updateField':
      return { ...state, [action.field]: action.value };
    case 'reset':
      return initialState;
    default:
      return state;
  }
}

function RegisterFormUseReducer() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (state.password !== state.confirmPassword) {
      alert('两次密码不一致!');
      return;
    }
    console.log(state);
    dispatch({ type: 'reset' });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={state.username}
        onChange={(e) => dispatch({ type: 'updateField', field: 'username', value: e.target.value })}
        placeholder="用户名"
      />
      <input
        value={state.email}
        onChange={(e) => dispatch({ type: 'updateField', field: 'email', value: e.target.value })}
        placeholder="邮箱"
      />
      <input
        type="password"
        value={state.password}
        onChange={(e) => dispatch({ type: 'updateField', field: 'password', value: e.target.value })}
        placeholder="密码"
      />
      <input
        type="password"
        value={state.confirmPassword}
        onChange={(e) => dispatch({ type: 'updateField', field: 'confirmPassword', value: e.target.value })}
        placeholder="确认密码"
      />
      <label>
        <input
          type="checkbox"
          checked={state.acceptTerms}
          onChange={(e) => dispatch({ type: 'updateField', field: 'acceptTerms', value: e.target.checked })}
        />
        同意条款
      </label>
      <button type="submit">注册</button>
      <button type="button" onClick={() => dispatch({ type: 'reset' })}>重置</button>
    </form>
  );
}

特点

  • 所有字段集中管理在 state
  • 重置、动态字段处理方便。
  • 更新逻辑统一在 reducer,可维护性高。

总结 : 为什么要用 useReducer

  • 当你有多个状态值 要管理,且它们之间有关联(比如同时更新多个字段)
  • 当更新逻辑复杂,用 useState 会很混乱
  • 想让状态更新逻辑更清晰、集中、可测试
相关推荐
恋猫de小郭18 分钟前
Redis 作者反驳「中国模型之所以强,是因为通过 API 蒸馏了美国模型」
前端·人工智能·ai编程
Darling噜啦啦22 分钟前
Canvas 游戏开发与数据可视化实战:从飞机大战到 ECharts 报表
前端·echarts·canvas
OpenTiny社区33 分钟前
这次更新太良心!GenUI SDK v1.2.0 轻量化 + 稳流式 + 超强 Playground
前端·vue.js·ai编程
梨子同志34 分钟前
WebGL test
前端
程序员黑豆36 分钟前
AI全栈开发系列开篇:从Java全栈到AI应用实战
前端·ai编程·全栈
yangyj39 分钟前
从 PDR 到落地:用 Codex 完成一次 Rspack 升级
前端
程序员鱼皮39 分钟前
提示词工程已死,Loop Engineering 称王!保姆级教程 + 项目实战
前端·后端·ai编程
小爷毛毛_卓寿杰1 小时前
给 Embedding 模型也加一块“游乐场“—— Xinference 是怎么把 vector 变成肉眼可见的体验的
前端
忆江南1 小时前
iOS 性能优化全面详解
前端
lichenyang4531 小时前
HAP / HAR / HSP 到底啥区别?顺带把「导入」那点疑惑讲清楚
前端