react的 useReducer 使用场景、替代 useState 的情况

useReducer 是 React 提供的状态管理 Hook,适合处理复杂状态逻辑 。很多场景用 useState 能写,但随着状态增多、更新规则复杂,代码会变乱,这时候 useReducer 更适合。


一、useReducer 基础概念

语法:

scss 复制代码
const [state, dispatch] = useReducer(reducer, initialState);

参数:

  • state:当前状态
  • dispatch(action):触发更新
  • reducer:状态处理函数
  • initialState:初始值

类似 Redux:

javascript 复制代码
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };

    case 'decrement':
      return { count: state.count - 1 };

    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0
  });

  return (
    <>
      <p>{state.count}</p>

      <button
        onClick={() =>
          dispatch({ type: 'increment' })
        }
      >
        +
      </button>

      <button
        onClick={() =>
          dispatch({ type: 'decrement' })
        }
      >
        -
      </button>
    </>
  );
}

二、什么时候 useState 不够用了?

场景1:多个相关状态一起维护(推荐 useReducer)

useState 写法

例如登录表单:

scss 复制代码
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');

提交:

scss 复制代码
setLoading(true);

api.login()
  .then(() => {
    setLoading(false);
  })
  .catch(() => {
    setError('失败');
    setLoading(false);
  });

状态多时:

go 复制代码
username
password
loading
error
success
token
userInfo

会越来越乱。


useReducer:

go 复制代码
const initialState = {
  username: '',
  password: '',
  loading: false,
  error: ''
};

function reducer(state, action) {
  switch (action.type) {
    case 'SET_USERNAME':
      return {
        ...state,
        username: action.payload
      };

    case 'LOGIN_START':
      return {
        ...state,
        loading: true
      };

    case 'LOGIN_FAIL':
      return {
        ...state,
        loading: false,
        error: action.payload
      };

    default:
      return state;
  }
}

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

更新:

php 复制代码
dispatch({
  type: 'SET_USERNAME',
  payload: '张三'
});

逻辑集中。

适合:

✅ 表单

✅ 登录流程

✅ 搜索筛选条件

✅ 弹窗状态


场景2:状态之间有关联

例如购物车:

状态:

复制代码
商品数量
总价
优惠金额
是否可提交

如果:

scss 复制代码
setCount()
setPrice()
setDiscount()
setCanSubmit()

可能出现更新不同步。


用 reducer:

javascript 复制代码
function reducer(state, action) {
  switch(action.type){

    case 'ADD_PRODUCT':

      const count =
        state.count + 1;

      return {
        count,
        total:
          count * state.price,
        canSubmit:
          count > 0
      }
  }
}

一次更新全部状态。

适合:

复制代码
购物车
订单
配置面板
步骤流

场景3:状态更新逻辑复杂

例如:

性能平台筛选条件:

复制代码
云厂商
CPU架构
规格
产品代际
核数
自动联动

你之前做过类似:

选择 CPU → 自动填规格 → 自动填云厂商

多个接口联动:

复制代码
选择规格
↓
请求接口
↓
更新5个状态
↓
重置表单

这类就适合 reducer:

php 复制代码
dispatch({
   type:'UPDATE_FILTER',
   payload:data
})

统一管理。


场景4:状态机(步骤流)

例如:

上传文件:

go 复制代码
idle
uploading
success
error

useReducer:

matlab 复制代码
function reducer(state, action) {

 switch(action.type){

   case 'UPLOAD':
      return {
        status:'loading'
      }

   case 'SUCCESS':
      return {
        status:'success'
      }

   case 'FAIL':
      return {
        status:'error'
      }
 }
}

比:

go 复制代码
loading
success
error

多个 bool 更清晰。


适合:

复制代码
上传
支付
审批流
登录
任务执行

场景5:多人协作,希望逻辑统一

useState

更新散落:

scss 复制代码
setA()
setB()
setC()

难找。


useReducer

统一:

bash 复制代码
dispatch({
  type:'UPDATE_USER'
})

所有更新都进 reducer。

维护成本低。


三、什么时候不要用 useReducer?

简单状态:

arduino 复制代码
const [visible, setVisible]
const [count, setCount]
const [name, setName]

没必要:

scss 复制代码
useReducer()

会增加复杂度。


建议:

状态复杂度 推荐
1~2 个简单状态 useState
多个相关状态 useReducer
状态联动 useReducer
复杂业务流程 useReducer
状态机 useReducer
大型组件 useReducer
全局状态 useReducer + Context

四、和 Redux 区别

很多人第一次看到:

scss 复制代码
dispatch()
reducer()
action.type

以为 Redux。

其实:

Redux:

复制代码
全局状态
所有组件共享

useReducer:

复制代码
组件内部状态
局部状态管理

组合使用:

复制代码
Context + useReducer

≈ 简化版 Redux

适合中小项目。


可以记一句经验:

useState 管"值",useReducer 管"状态变化规则"。

当你发现组件里出现很多:

scss 复制代码
setA()
setB()
setC()
setD()

或者多个状态互相影响时,就可以考虑换 useReducer。这在你做性能平台那种"筛选条件联动 + 接口回填"的页面,会比多个 useState 更容易维护。

相关推荐
谙忆10241 分钟前
WebCodecs 实战:用 ImageDecoder 和 VideoFrame 在浏览器里做硬件加速的帧处理
前端
JerrySir4 分钟前
密码没泄露,2FA 也没失效:Chrome 146 为什么还要让 Cookie“搬不走”?
前端
小蚂蚁i4 分钟前
React Hooks 原理深度解析:从会用到真正懂
前端·react.js
触底反弹10 分钟前
Vue 实战:手把手教你实现 ChatGPT 同款打字机效果
前端·javascript·人工智能
BreezeJiang12 分钟前
Vue 3 流式输出实战:ReadableStream + DeepSeek API 实现打字机对话
前端
用户EasyAdminBlazor18 分钟前
EasyAdminBlazor 第十篇:数据导入导出——批量处理不再麻烦
前端·后端
饮茶三千20 分钟前
电子保函模板编辑器实战二:HTML→FTL 编译转换的设计与实现
前端·vue.js·设计模式
ricardo197325 分钟前
SSR / SSG 性能对比:Next.js 三种渲染模式实测数据
前端·面试
ikoala41 分钟前
Codex 小白入门:从安装到插件、MCP、Skills,一篇把配置讲明白
前端·javascript·后端
雪隐1 小时前
用Flutter做背单词APP-04一个人,2周,从零撸出一个背单词App
前端·人工智能·后端