React

State Hooks - useReducer

useReducer 用于高效管理组件状态,尤其适用于复杂状态逻辑。通过分发动作来更新状态,使状态变更更加可预测和易于维护。
This entry is part 6 of the seriesenjoy-react-hooks

在之前的文章中我们学习了如何使用useState管理状态。本章我们将先学习另外一种管理状态的方式useReducer

useState 直接修改状态不同,useReducer 通过分发动作(action)来更新状态,这使得它更适合于处理涉及多个子值或需要触发复杂更新逻辑的状态,你可以将组件的所有状态更新逻辑整合到一个外部函数中,这个函数叫作 reducer

假设我们要实现以下功能:

Current value:
0

使用useState的版本如下:

Code Playground
import { useState } from 'react';

const MAX = 5;
const MIN = -5;

export default function App() {
  const [state, setState] = useState({count: 0, error: ''})

  const handleIncrement = () => {
    const newCount = state.count + 1
    const hasError = newCount > MAX
    setState({
      ...state, 
      count: hasError ?  state.count : newCount, 
      error: hasError ? 'Maximum reached' : '' 
    })
  }

  const handleDecrement = () => {
    const newCount = state.count - 1;
    const hasError = newCount < MIN;
    setState({
      ...state, 
      count: hasError ?  state.count : newCount, 
      error: hasError ? 'Minimum  reached' : '' 
    })
  }

  return (
    <main>
      <div>Current value:</div>
      <section>{state.count}</section>
      <section>{state.error}</section>

      <div>
        <button onClick={handleIncrement}>increment</button>
        <button onClick={handleDecrement}>decrement</button>
      </div>
    </main>
  )
}

下面让我们使用useReducer来重构上面的版本

第一步: 定义action, 它是一个普通的JS对象,至少需要包含一个可以表明发生了什么事情的字段,一般用type表示

type Action = {
type : 'increment' | 'decrement'
}

第二步: 定义reducer, reducer 函数就是你放置状态逻辑的地方。它接受两个参数,分别为当前 stateaction 对象,并且返回的是更新后的 state

type State = {
count: number;
error?: string
}
function reducer(state: State, action: Action) {
switch (action.type) {
case "increment": {
const newCount = state.count + 1
const hasError = newCount > MAX
return {
...state,
count: hasError ? state.count : newCount,
error: hasError ? 'Maximum reached' : ''
};
}
case "decrement": {
const newCount = state.count - 1;
const hasError = newCount < MIN;
return {
...state,
count: hasError ? state.count : newCount,
error: hasError ? "Minimum reached" : "",
};
}
default: {
return state;
}
}
}

第三步: 在组件中使用useRecuder

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

useReducer 钩子接受 2 个参数:

  • 一个 reducer 函数
  • 一个初始的 state

它返回如下内容:

  • 一个有状态的值
  • 一个 dispatch 函数(用来 “派发” 用户操作给 reducer

下面放出完整代码

Code Playground
import { useReducer } from 'react';

const MAX = 5;
const MIN = -5;

type State = {
  count: number;
  error?: string 
}

type Action = {
  type : 'increment' | 'decrement'
} 

function reducer(state: State, action: Action) {
  switch (action.type) {
    case "increment": {
      const newCount = state.count + 1
      const hasError = newCount > MAX
      return { 
        ...state, 
        count: hasError ?  state.count : newCount, 
        error: hasError ? 'Maximum reached' : '' 
      };
    }
    case "decrement": {
      const newCount = state.count - 1;
      const hasError = newCount < MIN;
      return {
        ...state,
        count: hasError ? state.count : newCount,
        error: hasError ? "Minimum reached" : "",
      };
    }
    default: {
      return state;
    }
  }
}
export default function App() {
  const [state , dispatch] = useReducer(reducer, {count: 0})

  return (
    <main>
      <div>Current value:</div>
      <section>{state.count}</section>
      <section>{state.error}</section>

      <div>
        <button onClick={() => dispatch({ type: "increment" })}>increment</button>
        <button onClick={() => dispatch({ type: "decrement" })}>decrement</button>
      </div>
    </main>
  )
}
Table of Contents

Questions? Let's chat

discord logoOPEN DISCORD
6423
members online
previous article
Ref Hooks - useRef & useImperativeHandle
next article
Transition Hooks - useTransition

A TypeScript Full Stack Blog

Share articles about Typescript, React, Next.js, Node.js and Css from time to time.
No spam, unsubscribe at any time.