从简单到复杂:何时用 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 会很混乱
  • 想让状态更新逻辑更清晰、集中、可测试
相关推荐
斯普信专业组2 小时前
2025 最好的Coze入门到精通教程(下)
前端·javascript·ui
德育处主任2 小时前
p5.js 圆弧的用法
前端·javascript·canvas
Arvin6273 小时前
Nginx IP授权页面实现步骤
服务器·前端·nginx
xw55 小时前
Trae安装指定版本的插件
前端·trae
默默地离开5 小时前
前端开发中的 Mock 实践与接口联调技巧
前端·后端·设计模式
南岸月明5 小时前
做副业,稳住心态,不靠鸡汤!我的实操经验之路
前端
嘗_5 小时前
暑期前端训练day7——有关vue-diff算法的思考
前端·vue.js·算法
MediaTea5 小时前
Python 库手册:html.parser HTML 解析模块
开发语言·前端·python·html
杨荧5 小时前
基于爬虫技术的电影数据可视化系统 Python+Django+Vue.js
开发语言·前端·vue.js·后端·爬虫·python·信息可视化
BD_Marathon6 小时前
IDEA中创建Maven Web项目
前端·maven·intellij-idea