初识React状态管理:useReducer

113 阅读5分钟

引言

小编最近也是忙的飞起,最近刚看了一下小米YU7,感觉找到了人生奋斗的目标。虽然说彩礼38.8W还没有赚到,但是先找到了一个买车的小目标。随着学习React的脚步不断深入,我们也是来到了React的比较难的部分,虽然还有一篇路由守卫的文章没写,但等下小编就会通宵达旦,挑灯夜读,将没有写的路由守卫将它写掉去,废话不多雪,让我们通过下面这篇文章来认识一下useReducer吧!


组件通信

在React组件通信当中,我们通常有几种方式:

  1. 父子组件: 通过props传递数据
  2. 子父组件: 通过回调函数传递数据
  3. 兄弟组件: 通过共同的父组件中转
  4. 跨层级组件(也就是叔叔阿姨组件):使用Context或Redux等全局方案
// 传统父子组件通信示例
function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <Child count={count} onIncrement={() => setCount(c => c + 1)} />
    </div>
  );
}

function Child({ count, onIncrement }) {
  return (
    <button onClick={onIncrement}>
      点击增加: {count}
    </button>
  );
}

但当应用复杂度增加时,这种“单向数据流”方式会变得难以维护。特别是当多个组件需要共享状态时,状态提升会导致“俄罗斯套娃”式的prop传递,这就是我们需要useReduceruseContext的原因。
在这里由于初学useReducer钩子,且小编自认为这里难度较大,所以useReducer和useContext的结合应用将会放到下一篇文章里面来讲。希望喜欢小编的读者可以关注一下小编,后续的文章很快将会出炉。


useReducer基础:更强大的状态管理

在这里大家是否会对此产生疑问,为何是更强大的状态管理呢,先不急,让我们先来初识一下useReducer❗

useReducer是什么

useReducer是React提供的一个Hook,它接受一个reducer函数和初始状态,返回当前状态和一个dispatch函数:

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

现在让我们来深入理解一下上述代码当中,state,dispatch,reducer,initialState四个单词的作用。

关键要点总结
概念作用特点使用场景
initialState设置初始状态值组件首次渲染时使用定义应用启动时的默认状态
reducer状态更新规则纯函数,无副作用集中管理所有状态转换逻辑
state当前应用状态响应式,触发重渲染UI渲染,派生状态计算
dispatch发送操作指令稳定引用,不触发额外渲染响应用户交互或系统事件

纯函数

纯函数是指符合以下特性的函数:

  1. 确定性:给定相同的输入,总是返回相同的输出,不管执行多少次
  2. 无副作用:不会修改函数外部的任何状态(如全局变量)
  3. 不依赖外部状态:只依赖于传入的参数,不使用函数外部的变量

你当前打开的文件中的add函数就是一个典型的纯函数例子:

function add(a,b){
  return a + b;
}

这个函数只依赖输入参数a和b,总是返回相同输入的相同结果,且不修改任何外部状态。

在React的reducer模式中,纯函数非常重要,因为它保证了状态变化的可预测性和可测试性,便于调试和维护。

核心概念

1.reducer函数:纯函数,接收当前的状态和action,返回新状态
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      // 新的状态
      return {
        count: state.count + 1
      }
    case 'decrement':
      return {
        count: state.count - 1
      }
    default:
      return state
  }
}
2.action对象:描述状态变化的普通对象,必须有type属性

下面是react官网上的一种代码书写格式

<button
  onClick={() => {
    dispatch({
        type: 'changed_selection',
        contactId: contact.id,     
    })
  }}>
  {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}
</button>

让我们来看看自己定义的action对象模式:

dispatch({ type: 'increment' })
dispatch({ type: 'incrementByNum', payload: count })
3.dispatch对象:描述状态变化的普通对象,必须有type属性
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <input type="text" value={count} onChange={(e) => setCount(e.target.value)} />
      <button
        onClick={() => dispatch({ type: 'incrementByNum', payload: count })}>
        incrementByNum
      </button>

完整计数器代码

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

// 管理好多
const initialState = {
  count: 0,
  isLogin: false,
  theme: 'light'
}
// 管理 分部门
// reducer纯函数 返回可靠的状态
// 状态生产器
// case 状态修改的规定 
const reducer = (state, action) => {
  // increment, decrement, 
  // {type: 'increment'}
  switch (action.type) {
    case 'increment':
      // 新的状态
      return {
        count: state.count + 1
      }
    case 'decrement':
      return {
        count: state.count - 1
      }
    case 'incrementByNum':
      return {
        count: state.count + parseInt(action.payload)
      }
    // 没有按照规矩来修改的话,不能对此产生修改
    default:
      return state
  }
}


function App() {
  // 初始值 initialValue
  // 当前的状态值 旧状态 新状态
  // 界面由当前状态来驱动
  const [count, setCount] = useState(0)

  // 大型项目
  // dispatch 派发 函数
  // 参数固定 {type: ''} action_type

  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <>
      <h1>count:{state.count}</h1>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <input type="text" value={count} onChange={(e) => setCount(e.target.value)} />
      <button
        onClick={() => dispatch({ type: 'incrementByNum', payload: count })}>
        incrementByNum
      </button>
    </>

  )
}

export default App

现在让我们来看到页面的效果图吧,没有写样式,稍微有些丑不拉几🤭🤭🤭

计数器reducer.gif


为什么useReducer更强大

这里牵扯到了useState和useReducer的使用比较,在这篇文章中就不过多叙述,对于useState的使用之后小编会单独出一篇文章专门说明这两个钩子的作用。我们还是通过表格对比的方式来学习他们到底该在什么情况之下使用他们!

useReducer vs useState:核心差异对比

维度useStateuseReducer
状态复杂度适合原始值/简单对象适合复杂对象/嵌套状态
更新逻辑分散在组件各处集中在reducer函数中
可测试性需要完整渲染组件测试可单独测试reducer逻辑
状态一致性需手动维护相关状态同步原子化更新确保状态一致性
可预测性中等(直接更新)高(纯函数更新)
扩展性有限强(支持中间件、高阶reducer等)
调试能力普通支持action日志、时间旅行调试

结语

useReducer是React提供的一种强大而灵活的状态管理方案,特别适合处理复杂的状态逻辑。通过遵循纯函数原则和合理的架构设计,你可以构建出可维护、可测试的React应用。掌握useReducer,你将在React状态管理的道路上迈出重要一步,为构建更复杂的应用打下坚实基础