
- ReactJS 教程
- ReactJS - 首頁
- ReactJS - 簡介
- ReactJS - 路線圖
- ReactJS - 安裝
- ReactJS - 特性
- ReactJS - 優點與缺點
- ReactJS - 架構
- ReactJS - 建立 React 應用
- ReactJS - JSX
- ReactJS - 元件
- ReactJS - 巢狀元件
- ReactJS - 使用新建立的元件
- ReactJS - 元件集合
- ReactJS - 樣式
- ReactJS - 屬性 (props)
- ReactJS - 使用屬性建立元件
- ReactJS - props 驗證
- ReactJS - 建構函式
- ReactJS - 元件生命週期
- ReactJS - 事件管理
- ReactJS - 建立一個事件感知元件
- ReactJS - 在 Expense Manager 應用中引入事件
- ReactJS - 狀態管理
- ReactJS - 狀態管理 API
- ReactJS - 無狀態元件
- ReactJS - 使用 React Hooks 進行狀態管理
- ReactJS - 使用 React Hooks 進行元件生命週期管理
- ReactJS - 佈局元件
- ReactJS - 分頁
- ReactJS - Material UI
- ReactJS - Http 客戶端程式設計
- ReactJS - 表單程式設計
- ReactJS - 受控元件
- ReactJS - 非受控元件
- ReactJS - Formik
- ReactJS - 條件渲染
- ReactJS - 列表
- ReactJS - Keys
- ReactJS - 路由
- ReactJS - Redux
- ReactJS - 動畫
- ReactJS - Bootstrap
- ReactJS - 地圖
- ReactJS - 表格
- ReactJS - 使用 Flux 管理狀態
- ReactJS - 測試
- ReactJS - CLI 命令
- ReactJS - 構建和部署
- ReactJS - 示例
- Hooks
- ReactJS - Hooks 簡介
- ReactJS - 使用 useState
- ReactJS - 使用 useEffect
- ReactJS - 使用 useContext
- ReactJS - 使用 useRef
- ReactJS - 使用 useReducer
- ReactJS - 使用 useCallback
- ReactJS - 使用 useMemo
- ReactJS - 自定義 Hooks
- ReactJS 高階
- ReactJS - 可訪問性
- ReactJS - 程式碼分割
- ReactJS - Context
- ReactJS - 錯誤邊界
- ReactJS - 轉發 Refs
- ReactJS - 片段
- ReactJS - 高階元件
- ReactJS - 整合其他庫
- ReactJS - 最佳化效能
- ReactJS - Profiler API
- ReactJS - 埠
- ReactJS - 無 ES6 ECMAScript 的 React
- ReactJS - 無 JSX 的 React
- ReactJS - 調和
- ReactJS - Refs 和 DOM
- ReactJS - 渲染 Props
- ReactJS - 靜態型別檢查
- ReactJS - 嚴格模式
- ReactJS - Web Components
- 其他概念
- ReactJS - 日期選擇器
- ReactJS - Helmet
- ReactJS - 內聯樣式
- ReactJS - PropTypes
- ReactJS - BrowserRouter
- ReactJS - DOM
- ReactJS - 走馬燈
- ReactJS - 圖示
- ReactJS - 表單元件
- ReactJS - 參考 API
- ReactJS 有用資源
- ReactJS - 快速指南
- ReactJS - 有用資源
- ReactJS - 討論
ReactJS - 使用 useReducer
useReducer hook 是 useState hook 的高階版本。眾所周知,useState 的目的是管理狀態變數。useState 返回一個函式,該函式接受一個值並使用給定值更新狀態變數。
// counter = 0 const [counter, setCounter] = useState(0) // counter = 1 setCounter(1) // counter = 2 setCounter(2)
useReducer hook 接受一個 reducer 函式以及初始值,並返回一個排程程式函式。Reducer 函式將接受初始狀態和一個 action(特定場景),然後提供根據 action 更新狀態的邏輯。排程程式函式接受 action(以及相應的詳細資訊)並使用提供的 action 呼叫 reducer 函式。
例如,useReducer 可用於根據增量和減量 action 更新計數器狀態。增量 action 將計數器狀態加 1,減量 action 將計數器狀態減 1。
讓我們在本節中學習如何在 React 中使用 useReducer hook。
useReducer hook 的簽名
useReducer hook 的簽名如下所示:
const [<state>, <dispatch function>] = useReducer(<reducer function>, <initial argument>, <init function>);
這裡,
state 表示要在狀態中維護的資訊
reducer 函式是一個 JavaScript 函式,用於根據 action 更新狀態。
以下是 reducer 函式的語法:
(<state>, <action>) => <updated state>
其中,
state - 當前狀態資訊
action - 要執行的操作(應有有效負載來執行操作)
更新後的狀態 - 更新後的狀態
初始引數 - 表示狀態的初始值。
init 函式 - 表示初始化函式,可用於設定狀態的初始值/重置狀態的當前值。如果需要計算初始值,則可以使用 init 函式。否則,可以跳過該引數。
應用 reducer hook
讓我們建立一個 React 應用來管理待辦事項的集合。首先,我們將使用 useState 實現它,然後將其轉換為使用 useReducer。透過使用這兩個 hook 實現應用程式,我們將瞭解 useReducer 相比 useState 的優勢。此外,我們還可以根據情況明智地選擇 hook。
首先,建立一個新的 React 應用並使用以下命令啟動它。
create-react-app myapp cd myapp npm start
接下來,在 component 資料夾下(src/components/TodoList.js)建立一個 React 元件 TodoList。
function TodoList() { return <div>Todo List</div> } export default TodoList
接下來,更新根元件 App.js 以使用新建立的 TodoList 元件。
import logo from './logo.svg'; import './App.css'; import TodoList from './components/TodoList'; function App() { return ( <div style={{ padding: "5px"}}> <TodoList /> </div> ); } export default App;
接下來,建立一個變數 todoData 來管理待辦事項列表,並使用 useState hook 將其設定為狀態。
const [todoData, setTodoData] = useState({ action: '', items: [], newItem: null, id: 0 })
這裡,
action 用於表示要應用於當前待辦事項列表 (items) 的當前操作,add 和 delete。
items 是一個用於儲存當前待辦事項列表的陣列。
newItem 是一個用於表示當前待辦事項的物件。該物件將有兩個欄位,id 和 todo。
id 是在刪除操作期間要刪除的當前專案的 ID。
useState 用於獲取和設定待辦事項列表 (todoData)。
接下來,渲染當前待辦事項列表 (todoData.items),以及一個刪除按鈕和一個輸入文字欄位以輸入新的待辦事項。
<div> <p>List of Todo list</p> <ul> {todoData.items && todoData.items.map((item) => <li key={item.id}>{item.todo} <span><button onClick={(e) => handleDeleteButton(item.id, e)}>Delete</button></span></li> )} <li><input type="text" name="todo" onChange={handleInput} /> <button onClick={handleAddButton}>Add</button></li> </ul> </div>
這裡,
從狀態變數 todoData 渲染當前待辦事項列表。
渲染一個輸入欄位,供使用者輸入新的待辦事項,並附加 onChange 事件處理程式 handleInput。事件處理程式將使用使用者在輸入欄位中輸入的資料更新 todo 狀態 (todoData) 中的 newItem。
渲染一個按鈕,供使用者將新輸入的待辦事項新增到當前待辦事項列表中,並附加 onClick 事件處理程式 handleAddButton。事件處理程式將把當前/新的待辦事項新增到待辦事項狀態中。
為待辦事項列表中的每個專案渲染一個按鈕,並附加 onClick 事件處理程式 handleDeleteButton。事件處理程式將從待辦事項狀態中刪除相應的待辦事項。
接下來,實現 handleInput 事件處理程式,如下所示:
const handleInput = (e) => { var id = 0 if(todoData.newItem == null) { for(let i = 0; i < todoData.items.length; i++) { if(id < todoData.items[i].id) { id = todoData.items[i].id } } id += 1 } else { id = todoData.newItem.id } let data = { actions: '', items: todoData.items, newItem: { id: id, todo: e.target.value }, id: 0 } setTodoData(data) }
這裡我們有,
使用使用者輸入的資料 (e.target.value) 更新 newItem.todo
建立並設定新專案的 ID。
接下來,實現 handleDeleteButton 事件處理程式,如下所示:
const handleDeleteButton = (deleteId, e) => { let data = { action: 'delete', items: todoData.items, newItem: todoData.newItem, id: deleteId } setTodoData(data) }
在這裡,處理程式將要刪除的待辦事項的 ID (deleteid) 和 delete 操作設定為待辦事項狀態。
接下來,實現 handleAddButton 事件處理程式,如下所示:
const handleAddButton = () => { let data = { action: 'add', items: todoData.items, newItem: todoData.newItem, id: 0 } setTodoData(data) }
在這裡,處理程式在狀態中設定新專案和 add 操作。
接下來,實現 add 操作,如下所示:
if(todoData.action == 'add') { if(todoData.newItem != null) { let data = { action: '', items: [...todoData.items, todoData.newItem], newItem: null, id: 0 } setTodoData(data) } }
在這裡,新專案新增到待辦事項狀態中現有的列表 (todoData.items) 中。
接下來,實現 delete 操作,如下所示:
if(todoData.action == 'delete' && todoData.id != 0) { var newItemList = [] for(let i = 0; i < todoData.items.length; i++) { if(todoData.items[i].id != todoData.id) { newItemList.push(todoData.items[i]) } } let data = { action: '', items: newItemList, newItem: null, id: 0 } setTodoData(data) }
在這裡,從待辦事項列表 (todoData.items) 中刪除了指定 ID 的專案。
元件的完整原始碼如下所示:
import { useState } from "react" function TodoList() { const [todoData, setTodoData] = useState({ action: '', items: [], newItem: null, id: 0 }) if(todoData.action == 'add') { if(todoData.newItem != null) { let data = { action: '', items: [...todoData.items, todoData.newItem], newItem: null, id: 0 } setTodoData(data) } } if(todoData.action == 'delete' && todoData.id != 0) { var newItemList = [] for(let i = 0; i < todoData.items.length; i++) { if(todoData.items[i].id != todoData.id) { newItemList.push(todoData.items[i]) } } let data = { action: '', items: newItemList, newItem: null, id: 0 } setTodoData(data) } const handleInput = (e) => { var id = 0 if(todoData.newItem == null) { for(let i = 0; i < todoData.items.length; i++) { if(id < todoData.items[i].id) { id = todoData.items[i].id } } id += 1 } else { id = todoData.newItem.id } let data = { action: '', items: todoData.items, newItem: { id: id, todo: e.target.value }, id: 0 } setTodoData(data) } const handleDeleteButton = (deleteId, e) => { let data = { action: 'delete', items: todoData.items, newItem: todoData.newItem, id: deleteId } setTodoData(data) } const handleAddButton = () => { let data = { action: 'add', items: todoData.items, newItem: todoData.newItem, id: 0 } setTodoData(data) } return ( <div> <p>List of Todo list</p> <ul> {todoData.items && todoData.items.map((item) => <li key={item.id}>{item.todo} <span><button onClick={(e) => handleDeleteButton(item.id, e)}>Delete</button></span></li> )} <li><input type="text" name="todo" onChange={handleInput} /><button onClick={handleAddButton}>Add</button></li> </ul> </div> ) } export default TodoList
這裡,應用程式使用 useState hook 來實現功能。
接下來,開啟瀏覽器並新增/刪除待辦事項。應用程式將按如下所示執行:

使用 useReducer
讓我們使用 useReducer 重新實現該功能。
首先,在 component 資料夾下(src/components/TodoReducerList.js)建立一個 React 元件 TodoReducerList。
function TodoReducerList() { return <div>Todo List</div> } export default TodoReducerList
接下來,更新根元件 App.js 以使用新建立的 TodoReducerList 元件。
import './App.css'; import TodoReducerList from './components/TodoReducerList'; function App() { return ( <div style={{ padding: "5px"}}> <TodoReducerList /> </div> ); } export default App;
接下來,實現一個 reducer 函式 todoReducer,它將接收兩個引數,如下所示:
當前待辦事項列表 (items)
操作 (action.type) 以及與操作相關的資訊 (action.payload)。對於新增操作 (action.type),有效負載 (action.payload) 將包含新的待辦事項,對於刪除操作 (action.type),它將包含要刪除的待辦事項的 ID。
reducer 將對待辦事項列表應用相關操作,並返回修改後的待辦事項列表,如下所示:
function todoReducer(items, action) { // action = { type: 'add / delete', payload: 'new todo item'} let newTodoList = [] switch (action.type) { case 'add': var id = 0 for(let i = 0; i < items.length; i++) { if(id < items[i].id) { id = items[i].id } } action.payload.id = id + 1 newTodoList = [...items, action.payload] break; case 'delete': for(let i = 0; i < items.length; i++) { if(items[i].id != action.payload.id) { newTodoList.push(items[i]) } } break; default: throw new Error() } return newTodoList }
這裡我們有,
使用 switch 語句處理操作。
Added 操作將有效負載新增到現有待辦事項列表中。
deleted 操作從現有待辦事項列表中刪除有效負載中指定的專案。
最後,函式返回更新後的待辦事項列表。
接下來,在 TodoReducerList 元件中使用新建立的 reducer,如下所示:
const [items, dispatch] = useReducer(todoReducer, [])
這裡,useReducer 接收兩個引數:a) reducer 函式和 b) 當前待辦事項列表,並返回當前待辦事項列表和一個排程程式函式 dispatch。排程程式函式 (dispatch) 應使用相關有效負載資訊與新增或刪除操作一起呼叫。
接下來,為輸入文字欄位(新的待辦事項)和兩個按鈕(新增和刪除)建立處理程式函式,如下所示:
const [todo, setTodo] = useState('') const handleInput = (e) => { setTodo(e.target.value) } const handleAddButton = () => { dispatch({ type: 'add', payload: { todo: todo } }) setTodo('') } const handleDeleteButton = (id) => { dispatch({ type: 'delete', payload: { id: id } }) }
這裡我們有,
使用 useState 管理使用者輸入 (todo)。
使用 dispatch 方法在相關的處理程式中處理 add 和 delete 操作。
在處理程式中傳遞特定於操作的有效負載。
接下來,更新渲染方法,如下所示:
<div> <p>List of Todo list</p> <ul> {items && items.map((item) => <li key={item.id}>{item.todo} <span><button onClick={(e) => handleDeleteButton(item.id)}>Delete</button></span></li> )} <li><input type="text" name="todo" value={todo} onChange={handleInput} /> <button onClick={handleAddButton}>Add</button></li> </ul> </div>
元件的完整原始碼以及 reducer 函式如下所示:
import { useReducer, useState } from "react" function todoReducer(items, action) { // action = { type: 'add / delete', payload: 'new todo item'} let newTodoList = [] switch (action.type) { case 'add': var id = 0 for(let i = 0; i < items.length; i++) { if(id < items[i].id) { id = items[i].id } } action.payload.id = id + 1 newTodoList = [...items, action.payload] break; case 'delete': for(let i = 0; i < items.length; i++) { if(items[i].id != action.payload.id) { newTodoList.push(items[i]) } } break; default: throw new Error() } return newTodoList } function TodoReducerList() { const [todo, setTodo] = useState('') const [items, dispatch] = useReducer(todoReducer, []) const handleInput = (e) => { setTodo(e.target.value) } const handleAddButton = () => { dispatch({ type: 'add', payload: { todo: todo } }) setTodo('') } const handleDeleteButton = (id) => { dispatch({ type: 'delete', payload: { id: id } }) } return ( <div> <p>List of Todo list</p> <ul> {items && items.map((item) => <li key={item.id}>{item.todo} <span><button onClick={(e) => handleDeleteButton(item.id)}>Delete</button></span></li> )} <li><input type="text" name="todo" value={todo} onChange={handleInput} /> <button onClick={handleAddButton}>Add</button></li> </ul> </div> ) } export default TodoReducerList
接下來,開啟瀏覽器並檢查輸出。

我們可以清楚地理解,與純 useState 實現相比,useReducer 實現更簡單、更容易理解。
總結
useReducer 鉤子在狀態管理中引入了 reducer 模式。它鼓勵程式碼複用,並提高元件的可讀性和可理解性。總的來說,useReducer 是 React 開發者工具箱中一個必不可少且強大的工具。