# React Hook - useReducer ###### tags: `Javascript, React` > useReducer 基本上用來處理更複雜的 state 更新以及管理 # 範例說明: 我們簡單藉由 counter 來介紹 useReducer 的功能吧 引入 useReducer 之後,在內部放的參數有兩個 1. reducer 函式,會更新我們 state 2. 初始值(一般會使用物件型態) useReducer 會有兩個返回值 1. state (也就是目前的初始值) 2. dispatch (它會呼叫 reduer 方法來更新 state ) function reducer 主要會使用兩個參數,並且會返回更新後的 state 1. current state 2. action (會是 dispatch 來呼叫來更新 state 的方法) 每次我們觸發 dispatch 時就會觸發 ```javascript= function reducer(state, action){ return {count:state.count +1} } ``` 讓目前的 state.count 數值 + 1 ```javascript= import React,{useReducer} from 'react'; function reducer(state, action){ return {count:state.count +1} } export default function App() { const [state, dispatch] = useReducer(reducer, {count:0}) function increment(){ dispatch(); } return ( <> <span>{state.count}</span> <button onClick={increment}>+</button> </> ); } ``` 但是如果有複數的 dispatch 的時候就必須給他們名字(type),並且在針對複數的方法們使用條件式做篩選範例 針對 dispatch type 使用 switch 篩選並且返回邏輯 ```javascript= import React,{useReducer} from 'react'; function reducer(state, action){ switch(action.type){ case 'increment': return { count:state.count+1} case 'decrement': return { count:state.count-1} default: return state } } export default function App() { const [state, dispatch] = useReducer(reducer, {count:0}) function increment(){ dispatch({ type:'increment' }); } function decrement(){ dispatch({ type:'decrement' }); } return ( <> <button onClick={decrement}>-</button> <span>{state.count}</span> <button onClick={increment}>+</button> </> ); } ``` 就可以正常使用 counter 並且只能透過我們寫好的 action 來更新 state ,這樣一來就可以確保你的 state 的改變是可預期的也就更方便管理 ![](https://i.imgur.com/x7E5tLp.png) 這邊可以看到我們 type 的名字,都是直接使用字串填入散佈在程式碼中,但是可以使用變數的方式集中管理,這樣我們修改起來更為方便 ```javascript= import React,{useReducer} from 'react'; // 把 type 名字集中到這邊用變數處理 const ACTIONS ={ INCREMENT:'increment', DECREMENT:'decrement' } function reducer(state, action){ switch(action.type){ case ACTIONS.INCREMENT: return { count:state.count+1} case ACTIONS.DECREMENT: return { count:state.count-1} default: return state } } export default function App() { const [state, dispatch] = useReducer(reducer, {count:0}) function increment(){ dispatch({ type:ACTIONS.INCREMENT }); } function decrement(){ dispatch({ type:ACTIONS.DECREMENT }); } return ( <> <button onClick={decrement}>-</button> <span>{state.count}</span> <button onClick={increment}>+</button> </> ); } ``` 其實上面的範例用 useReducer 是相對麻煩的,但是主要它發光發熱的地方會在比較大型的專案裡面去管理比較大量的 state ,所以接下來會簡單用一個 todolist 的範例繼續解釋 useReducer 的用法 # 範例說明 2: 主要實作簡單的 todolist ,會有簡單的新增、刪除、顯示完成三個功能 ## App.js 這篇可以看到一些 payload 的使用 在 dispatch 使用下,傳入特定的參數 payload 1. 新增 todo 傳入 todo 本身(name 代表 todo 內容) 2. toggle todo 則傳入 `todo.id` 用來確認操作在正確的 todo 下(Todo.js 檔案內) 3. 刪除 todo 一樣傳入 `todo.id` 用來確認操作在正確的 todo 下(Todo.js 檔案內) 特別的地方是在要 map 出 todo list 的部分這邊傳下去的 props 其中 dispatch 部分非常的方便,可以直接透過這個 props 就可以使用 reducer function 裡面全部的 action delete todo, toggle todo 等等 ```javascript= import React,{useReducer, useState} from 'react'; import Todo from './Todo' export const ACTIONS ={ ADD_TODO:'add-todo', TOGGLE_TODO:'toggle-todo', DELETE_TODO:'delete-todo' } function reducer(todos, action){ switch(action.type){ case ACTIONS.ADD_TODO: return [...todos, newTodo(action.payload.name)] case ACTIONS.TOGGLE_TODO: return todos.map((todo)=>{ if(todo.id === action.payload.id){ return {...todo, completed: !todo.completed} } return todo; }) case ACTIONS.DELETE_TODO: return todos.filter(todo => todo.id !== action.payload.id) default: return todos } } function newTodo(name){ return { id:Date.now(), name:name, completed:false } } export default function App() { const [todos, dispatch] = useReducer(reducer, []) const [name, setName] = useState('') function handleSubmit(e){ e.preventDefault(); dispatch({type:ACTIONS.ADD_TODO, payload:{name:name}}); setName(''); } return ( <> <form onSubmit={handleSubmit}> <input value={name} onChange={e=>setName(e.target.value)}></input> </form> { todos.map(todo=>{ return <Todo key={todo.id} todo={todo} dispatch={dispatch}></Todo> }) } </> ); } ``` ## Todo.js 這邊是呈現 todo list 的部分 這邊直接使用 dispatch 內的 action 相當的方便不需要再額外引入別的內容直接使用 props 傳入的即可 * style 的部分使用邏輯運算子來做簡單的判斷 completed 的狀態,來辨別任務是否完成 * 下方的兩個按鈕主要針對其 click 事件觸發對應的 type 之外,也傳入需要的 payload ```javascript= import React from 'react' import {ACTIONS} from './App' export default function Todo({ todo, dispatch}) { return ( <div> <span style={{color: todo.completed ? '#AAA':'#000' }}> {todo.name} </span> <button onClick={() =>dispatch({type:ACTIONS.TOGGLE_TODO, payload: {id:todo.id}})}>Toggle</button> <button onClick={() =>dispatch({type:ACTIONS.DELETE_TODO, payload: {id:todo.id}})}>delete</button> </div> ) } ```