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

相关推荐
YAwu1110 小时前
原型与原型链:面试中的关键问题深入剖析
前端·javascript
To_OC10 小时前
徒手撸极简前后端分离Demo!吃透原生JS动态渲染底层
前端·javascript
HYCS10 小时前
用pixi.js实现fabric.js(四):StaticCanvas
前端·javascript·canvas
烬羽10 小时前
《读<JavaScript语言精粹>第3章,我整理了6个必须掌握的对象核心知识点》
前端
GuWenyue10 小时前
从零搭建用户管理系统!60分钟搞定RESTful接口+Bootstrap语义化首页
前端·后端
超人气王10 小时前
JavaScripts入门篇————js原型的底层原理
前端·javascript
蜡笔小电芯10 小时前
【Electron】第1章—新建工程(基于 Electron + Vite + JavaScript)
前端·javascript·electron
_xaboy10 小时前
开源Vue组件 FormCreate 使用组件内部方法校验
前端·vue.js·开源
审判长烧鸡11 小时前
【AI问答/前端】前端满天过海局(一)
前端·vue·浏览器