iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Modern Web

現在就學React.js 系列 第 27

React useReducer 進階狀態管理 -Day27

  • 分享至 

  • xImage
  •  

useReducer 是 React 提供的 Hook,用於處理更複雜的狀態管理。與 useState 相比,它引入了 reducer 函數來決定狀態更新的方式,這讓狀態管理流程更加可控與結構化。

本文將介紹 useReducer 的基本用法,並進一步優化之前Day20做的 TodoList小專案。

為什麼選擇 useReducer

  • 處理複雜邏輯:當狀態邏輯變得複雜,使用useReducer 能夠清晰地處理狀態更新。
  • 狀態集中管理:所有狀態管理集中在 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 函數特性
    1. dispatch 沒有返回值,只負責發送動作。
    2. dispatch 的狀態更新不會立即反映在當前程式碼中。它會等待事件執行完成後再批量更新。
    3. 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;

程式碼解析

  1. 初始狀態 initialState:定義計數器的初始狀態 { count: 0 }
  2. reducer 函數:根據不同的動作類型(action.type)來更新狀態值。
  3. useReducer Hook:將 reducer 函數與初始狀態 initialState 傳入 useReducer,並取得當前狀態 state 和發送動作的 dispatch 函數。
  4. 使用 dispatch 來更新狀態:通過點擊「+」或「-」按鈕來發送 INCREMENTDECREMENT 動作,從而更新計數器狀態。

看完上述的範例後,對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 狀態管理工具。

參考資料:

後記

本文將會同步更新到我的部落格

黃禎平 – Medium


上一篇
打造自己的 Hook:Custom Hook Day26
下一篇
React Redux 進階狀態管理工具 - Day28
系列文
現在就學React.js 31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言