《useReducer完全指南:复杂状态管理的终极方案》

212 阅读7分钟

📜 前言

在 React 开发中,状态管理是一个核心概念。useState 是最基础也是最常用的状态管理 Hook,说实话,用useState也可以很好地完成状态管理,但当应用状态变得复杂时,useReducer 往往能提供更优雅的解决方案。本文将深入探讨 useReducer 的工作原理、使用场景以及最佳实践。

❓ 什么是useReducer?

useReducer 是 React 提供的一个内置 Hook,它是一个高级Hook,它不像其它useEffect、useState等必须Hook一样,没有它我们也能完成项目的开发,但是有了它,可以写出更可维护、更可测试的 React 组件,它提供了比 useState 更结构化的状态更新方式,同时保持了 React 的简洁性。

🔍详细介绍useReducer

useState大家应该都很熟悉对吧,为了理解方便,下面我们就用与useState类比的方式来介绍useReducer吧!

首先看useState的API使用:

const [count,setCount] = useState(0);

咱们再给出useReducer的API使用:

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

很相似对吧!有几处不同,我们分别介绍


initialState

useReduceruseState一样,都是一个函数,但是它的参数是reducer,initialState

这里的reducer我们待会再说,先看看initialState

useReducer里的initialStateuseState里面的0是一样的,都是初始值

只不过,我们说useReducer适用于复杂的项目的状态管理,这里的initialState往往值得就不是0,1,2,3或者普通的字符串这些简单的初始值了,往往我们在initialState里面会放一些对象等,比如

const initialState = {
  count:0,
  isLogin:false,
  theme:'light'
}
const [state,dispatch] = useReducer(reducer,initialState)

这是我们平常在使用useReducer管理的一个案例,你可以看到initialState是一个对象,会放一些重要的内容供它管理。

当然了,这里只是常见的业务场景,事实上,不论是initialState还是useState的初始值,我们放对象和普通值都是可以的!


reducer

接下来讲解另一个参数reducer,它是一个函数,负责处理状态更新的逻辑,它的结构如下:

reducer 函数的结构

一个典型的 reducer 函数通常包含以下部分:

function reducer(state, action) {
  switch (action.type) {
    case 'ACTION_TYPE_1':
      // 处理第一种动作
      return { ...state, /* 更新部分状态 */ };
    case 'ACTION_TYPE_2':
      // 处理第二种动作
      return { ...state, /* 更新部分状态 */ };
    default:
      // 如果 action 不匹配任何 case,返回原状态
      return state;
  }
}

你可以看到:通常它的结构中,会有switch结构,对于不同的动作,它会进行限制,举个例子:

const reducer = (state,action) => {
  switch(action.type) {
    case 'increment':
    return {
      count: state.count + 1
    }
    case 'decrement':
      return {
        count: state.count-1
      }
    default:
      return state;
  }
}

对于相加和相减操作incrementdecrement,它会进行代理,假如某个button想操作状态state,在传统的useState中,它可能就直接setState了,但是在useReduer中,正是有const [state,dispatch] = useReducer(reducer,initialState) 里面的reducer函数存在,它就不会那么轻易直接setState,而是根据不同的操作(相加or相减)来限制住

特别是在大型项目开发中,对于用户金额这种敏感数据,可不能直接setState等,万一程序员哪天开发的时候头脑昏昏的,一不小心设置成了啥数字,可是容易挨骂的,而用useReduer管理这种敏感数据,由于reduer函数的存在,使得我们可以更放心地管理敏感数据!!!

所以reducer函数在设计时,需要是一个纯函数!

纯函数:reducer 不应该有副作用(如 API 调用、修改外部变量等),相同的输入应该总是产生相同的输出。

核心特征

  1. 相同输入总是返回相同输出

    • 只要输入参数相同,无论调用多少次、何时调用,输出结果必定相同
  2. 无副作用

    • 不会改变任何外部状态(不修改传入的参数、全局变量等)
    • 不会产生外部可观察的变化(如网络请求、IO操作等)

dispatch

现在,正式来介绍:const [state,dispatch] = useReducer(reducer,initialState) 里面的dispatch

大家知道const [state,setState] = useState("0")里面的setState是用来更改响应式状态state的对吧

dispatch和它的位置一样,它们的功能也是相似的,dispatch是用来触发状态更改的,只不过,状态真正的更改是在reducer,它不能像setState一样直接更改,它会被reducer给限制住。

dispatch 函数:

  • 接收一个 action 对象 作为参数
  • 将当前状态和这个 action 传递给 reducer 函数
  • 用 reducer 返回的新状态更新组件

看一个例子你就明白了:

import { 
  useState,
  useReducer
 } from 'react'
import './App.css'


//reducer拿到的action就是来自于dispatch的
const reducer = (state,action) => {
  switch(action.type) {
    case 'increment':
    return {
      count: state.count + 1
    }
    case 'decrement':
      return {
        count: state.count-1
      }
    default:
      return state;
  }
}

const initialState = {
  count:0,
}

function App(){
  const [count,setCount] = useState(0);
  const [state,dispatch] = useReducer(reducer,initialState)
  return (  
    <>
      <p>Count:{state.count}</p>
      //dispatch 接受一个{type:事件类型}为参数
      <button onClick={() => dispatch({type:'increment'})}>+</button>
      <button onClick={() => dispatch({type:'decrement'})}>-</button>
    </>
  )
}

export default App

在这个数字修改案例中,dispatchreducer就像魂斗罗一样,紧密配合

我们点击button按钮尝试增加or减少数字count,这个时候触发的action传递给dispatch,然后dispatch再将这个传递给reducer,触发真正的更改逻辑,reducer通过switch检测到,增加或减少,替代完成修改

同样,我们还可以触发reducer的同时携带数据:比如

 const [state,dispatch] = useReducer(reducer,initialState)
 const [count,setCount] = useState("");
 return
 (
 省略重复内容...
 //触发事件时携带数据
 <input type="text" value={count} onChange={(e) => setCount(e.target.value)}/>
 // payload会放在action发送给reduer
 <button onClick={() => dispatch({type:'incrementByNum',payload:count})}>
 incrementByNum
 </button>
 )
const reducer = (state,action) => {
  switch(action.type) {
     省略重复内容...
      //当携带数据payload时
    case 'incrementByNum':
      return {
        count: state.count + parseInt(action.payload)
      }
  }
}

所以我们总结一下dispatch的核心特性:

  1. 触发状态更新

    dispatch({ type: 'increment' });
    
  2. 不可变性保证

    • 不会直接修改当前 state
    • 总是通过 reducer 返回新状态
  3. 稳定引用

    • 在组件生命周期内,dispatch 函数的引用保持不变
    • 可以安全地放入依赖数组或传递给子组件

🌟重要应用

上面介绍了useReduer的API知道了useReduer的API const [state,dispatch] = useReducer(reducer,initialState),当你知道API中不同部分的意义和使用后,现在,对于useReduer你也能够很清楚它可以保护一些重要的数据,通过限制它们的修改操作来实现

下面再总结一下useReduer的几个重要应用:

1. 管理复杂表单状态(比 useState 更清晰)

// 传统方式:多个 useState
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)

// useReducer 方式:统一管理
const formReducer = (state, action) => {
  switch(action.type) {
    case 'UPDATE_FIELD':
      return { ...state, [action.field]: action.value }
    case 'SUBMIT':
      return { ...state, isSubmitting: true }
    // ...
  }
}

✓ 好处:所有表单状态在一个对象里,更新逻辑集中管理

2. 实现撤销/重做功能(超实用!)

const historyReducer = (state, action) => {
  const { past, present, future } = state
  switch(action.type) {
    case 'UNDO':
      return {
        past: past.slice(0, -1),
        present: past[past.length - 1],
        future: [present, ...future]
      }
    // ...
  }
}

✓ 好处:轻松记录状态历史,实现时间旅行效果

3. 处理异步操作(如API请求)

const fetchReducer = (state, action) => {
  switch(action.type) {
    case 'FETCH_START':
      return { ...state, loading: true }
    case 'FETCH_SUCCESS':
      return { data: action.payload, loading: false, error: null }
    case 'FETCH_ERROR':
      return { ...state, loading: false, error: action.error }
  }
}

// 使用:清晰表达请求的不同阶段
dispatch({ type: 'FETCH_START' })
try {
  const data = await fetchData()
  dispatch({ type: 'FETCH_SUCCESS', payload: data })
} catch(error) {
  dispatch({ type: 'FETCH_ERROR', error })
}

✓ 好处:让异步状态变化更有条理,避免混乱的setState

这些应用场景展示了useReducer如何让你的React代码更:

  • 有条理(逻辑集中管理)
  • 可维护(更新规则一目了然)
  • 可扩展(轻松添加新功能)

✅总结

useReducer就像一个公司会计一样,时刻管理着数据状态,当我们要增加数据状态时,需要向这个公司会计发送action请求,它接收到,同意了之后才能按规定增加,减少和修改也是一样...

这篇文章像是一篇useReducer的指南,通过这篇文章你能够了解其的概念和大致使用方法与场景,但是useReducer是一个相对复杂和困难的hook,如果想要了解更多它的深入知识,还是建议去阅读react官方文档进行深入了解哦!