从简单到复杂:何时用 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 会很混乱
  • 想让状态更新逻辑更清晰、集中、可测试
相关推荐
Zuckjet_7 小时前
开启 3D 之旅 - 你的第一个 WebGL 三角形
前端·javascript·3d·webgl
2401_863801467 小时前
探索 12 种 3D 文件格式:综合指南
前端·3d
珍宝商店9 小时前
前端老旧项目全面性能优化指南与面试攻略
前端·面试·性能优化
bitbitDown9 小时前
四年前端分享给你的高效开发工具库
前端·javascript·vue.js
gnip10 小时前
实现AI对话光标跟随效果
前端·javascript
脑花儿11 小时前
ABAP SMW0下载Excel模板并填充&&剪切板方式粘贴
java·前端·数据库
lumi.12 小时前
Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
前端·javascript·vue.js
二十雨辰12 小时前
vue核心原理实现
前端·javascript·vue.js
影子信息12 小时前
[Vue warn]: Error in mounted hook: “ReferenceError: Jessibuca is not defined“
前端·javascript·vue.js
卷Java12 小时前
CSS模板语法修复总结
java·前端·css·数据库·微信小程序·uni-app·springboot