useReducer 是 React 提供的 Hook,用於處理更複雜的狀態管理。與 useState 相比,它引入了 reducer 函數來決定狀態更新的方式,這讓狀態管理流程更加可控與結構化。
本文將介紹 useReducer 的基本用法,並進一步優化之前Day20做的 TodoList小專案。
useReduceruseReducer 能夠清晰地處理狀態更新。reducer 中,使狀態管理流程更加結構與可讀性。useReducer 的基本語法const [state, dispatch] = useReducer(reducer, initialState);
reducer:處理狀態更新的函數,它接收當前狀態(state)和動作(action),並返回更新的狀態。initialState:定義元件初始的狀態。dispatch:發送動作(action)來觸發狀態更新。dispatch 函數的運作useReducer 返回的 dispatch 函數用來更新 state 並觸發元件重新渲染。它需要傳入一個 action 物件作為參數。範例如下:
// Reducer 函數
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 });
	
	function handleClick() {
	  dispatch({ type: 'INCREMENT' });
	}
	
	...//略
}
action:通常是包含 type 屬性與其他附加資料的對象。type 用來標識要執行的動作,附加屬性可以用來攜帶額外的更新數據。dispatch 函數特性:
dispatch 沒有返回值,只負責發送動作。dispatch 的狀態更新不會立即反映在當前程式碼中。它會等待事件執行完成後再批量更新。state 的新值與舊值相同時,React 會跳過元件及其子元件的重新渲染,從而優化效能。useReducer 的使用我們來看一個計數器範例,說明如何使用 useReducer 來管理元件狀態。
useReducer 來實作計數器
import React, { useReducer } from 'react';
// 定義初始狀態
const initialState = { count: 0 };
// 定義 reducer 函數來描述狀態變更邏輯
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() {
  // 使用 useReducer 來管理狀態
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <p>當前計數:{state.count}</p>
      {/* 使用 dispatch 來發送 action */}
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
    </div>
  );
}
export default Counter;
initialState:定義計數器的初始狀態 { count: 0 }。reducer 函數:根據不同的動作類型(action.type)來更新狀態值。useReducer Hook:將 reducer 函數與初始狀態 initialState 傳入 useReducer,並取得當前狀態 state 和發送動作的 dispatch 函數。dispatch 來更新狀態:通過點擊「+」或「-」按鈕來發送 INCREMENT 或 DECREMENT 動作,從而更新計數器狀態。看完上述的範例後,對useReducer 有基本的認識與操作後,我們來重構先前在 Day18 時,有實作的綜合練習-TodoList,將它從 useState 重構為用 useReducer 做狀態管理吧!
先做定義初始狀態以及定義Reducer
// 定義初始狀態
const initialState = {
  todos: [
    { id: 1, text: '學習 React', completed: false },
    { id: 2, text: '完成作業', completed: false },
  ],
}
// 定義 Reducer
const todoReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [
          ...state.todos,
          { id: Date.now(), text: action.payload, completed: false },
        ],
      }
    case 'TOGGLE_COMPLETE':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        ),
      }
    case 'DELETE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload),
      }
    default:
      return state
  }
}
再更改原先的 useState 狀態 調整為 useReducer  ,再加上 dispatch 方法
const [state, dispatch] = useReducer(todoReducer, initialState)
  // 從 useReducer 中取得的 todos 狀態
  const {todos} = state
  // 定義 dispatch action 的方法
  const addTodo = text => {
    dispatch({type: 'ADD_TODO', payload: text})
  }
  const toggleComplete = id => {
    dispatch({type: 'TOGGLE_COMPLETE', payload: id})
  }
  const deleteTodo = id => {
    dispatch({type: 'DELETE_TODO', payload: id})
  }
這樣就更改完畢了,可以先試試看自己更改看看喔,若發現調整後有狀況,可參考下方的完整程式碼。
附上完整程式碼:Preview - nodebox - CodeSandbox
使用 useReducer 讓我們的狀態管理邏輯更加集中和明確,特別是在處理複雜應用邏輯時,它能夠有效簡化狀態更新的流程。在簡單專案中,useState 足夠應對,但當專案擴展時,useReducer 是更合適的選擇,讓狀態更新更加可控。
useReducer 它類似於 Redux 的 reducer 概念,明日將會介紹 Redux 狀態管理工具。
本文將會同步更新到我的部落格