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 更容易维护。

相关推荐
我命由我123453 小时前
CesiumJS 笔记 - 获取容器中心点、Cartesian3 clone 方法、修改 Cartesian3 对象的高度
前端·javascript·css·前端框架·html·html5·js
Hopebearer_4 小时前
页面突然只剩 DOM?一次静态资源版本错配排查
前端·部署
抱抱宝4 小时前
Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
javascript·人工智能·gpt·react.js·prompt·agent
东风破_5 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
圣殿骑士-Khtangc6 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
BigTopOne7 小时前
【ijkplayer】 硬解码流程
前端
kyriewen8 小时前
DeepSeek Harness开源第一天我就上手了——和Claude Code的差距比想象中大
前端·ai编程·deepseek
捡田螺的小男孩8 小时前
什么是 Skill?手把手带你写一个简单有用的 Skill!
前端·后端·程序员
IT_陈寒8 小时前
Redis集群这个坑,差点让我通宵
前端·人工智能·后端
90后的晨仔8 小时前
uni-app 路由跳转与页面导航 —— 终极详解(对标 iOS / Android / 鸿蒙三端原生)
前端