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
函數特性:
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 狀態管理工具。
本文將會同步更新到我的部落格