useReducer
- React useState: simple State
- React useReducer: complex State
- React useContext: global State
useState 容易管理單一狀態,但如果需要管理多個狀態,讓他比較好維護,這時候可以考慮使用 useReducer
比如:
const initFirstUser = {
id: "0391-3233-3201",
firstName: "Bill",
lastName: "Wilson",
city: "Missoula",
state: "Montana",
email: "bwilson@mtnwilsons.com",
admin: false
};
用法
(state, action) => newState
useReducer 接受一個 (state, action) => newState ,然後回傳現在的 state 以及其配套的 dispatch 方法。
(如果你熟悉 Redux,你已經知道這如何運作。)
用 reducer 寫個範例:

一開始先初始化 initialValue
再來就是寫出如何更改狀態的方法
import React, { useReducer } from "react";
const initialValue = { count: 0 };
const reducer = (state, { type }) => {
switch (type) {
case "add":
return { ...state, count: state.count + 1 };
case "minus":
return { ...state, count: state.count - 1 };
case "reset":
return { ...state, count: 0 };
default:
return state;
}
};
每當使用者點擊某個按鈕,就會發出一個動作來更新計數值 count,頁面就會展示更新之後 count。
export default function App() {
const [state, dispatch] = useReducer(reducer, initialValue);
return (
<div className="App">
<h1>Hello useReducer</h1>
<h2>{JSON.stringify(state)}</h2>
<button onClick={() => dispatch({ type: "add" })}>add</button>
<button onClick={() => dispatch({ type: "minus" })}>minus</button>
<button onClick={() => dispatch({ type: "reset" })}>reset</button>
</div>
);
}
React 確保 dispatch function 本身是穩定的,而且不會在重新 render 時改變。這就為什麼可以安全地從 useEffect 或 useCallback 的依賴列表省略它。
如果你在 Reducer Hook 回傳的值與目前的 state 相同,React 將會跳過 child component 的 render 及 effect 的執行。
(React 使用 Object.is 比較演算法。)
文本輸入(input)
用 useState 的寫法
import React, { useState } from "react";
export default function App() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
return (
<div className="App">
<h2>{JSON.stringify({ email, password }, null, 2)}</h2>
<label>
<input
onChange={(e) => setEmail(e.target.value)}
value={email}
type="text"
/>
</label>
<label>
<input
onChange={(e) => setPassword(e.target.value)}
value={password}
type="text"
/>
</label>
</div>
);
}
UI State: useSate custom hook 寫法 :(Re-using logic)取共用 logic 來做抽象,可以被重複使用,處理數據邏輯之類的東西。通過創建自定義鉤子,將它們從元件中取出。(Not UI State)
Render prpos 寫法 : (Re-using layout)可以僅重新渲染相關內容,將需要重用的任何狀態邏輯放入 Container 元件中,舉例 :可以用於創建多個表單的表單佈局,但是每個表單具有不同的子元件。
如果要隔離 jsx 的一部分並註入一些狀態而不給元件帶來副作用,則 render props 非常有用。
文本輸入(input)
用 useReducer 的寫法
const initialValue = { email: "", password: "" };
const reducer = (state, { type, payload }) => {
return { ...state, [type]: payload };
};
reducer 會預期收到 action 和 type,type 和 payload 用來回傳 current state 和 [type]: payload
export default function App() {
const [state, dispatch] = useReducer(reducer, initialValue);
return (
<div className="App">
<h2>{JSON.stringify(state, null, 2)}</h2>
<label>
<input
type="text"
value={state.email}
onChange={(e) => dispatch({ type: "email", payload: e.target.value })}
/>
</label>
<label>
<input
type="text"
value={state.password}
onChange={(e) =>
dispatch({ type: "password", payload: e.target.value })
}
/>
</label>
</div>
);
}
onChange 事件可以改成 接受 name 和 value
const onChange = ({ target: { name, value } }) => {
dispatch({ type: name, payload: value });
};
.
.
.
return (
<>
<input
value={state.email}
name="email"
onChange={onChange}
/>
</>
)
input 也可以改成 接受 value, type, onChange 事件
const InputTextfield = ({ value, type, onChange }) => {
return <input value={value} type={type}
onChange={onChange} />;
};
.
.
.
return (
<>
<InputTextfield
value={state.email}
type="text"
onChange={(e) => dispatch({ type: "email", payload: e.target.value })}
/>
</>
)
useState vs. useReudcer
- useStatet :適合管理簡單狀態
- useReducer : 適合複雜的狀態對像或狀態轉換(要保持可維護性和可預測性)
useContext v.s. useReudcer
- useContext :可訪問訪問狀態,避免一層的傳遞狀態。
- useReducer : 用過 Redux 肯定不會陌生,它主要用於更新復雜邏輯的狀態。
p.s. 新手會認為 context 本身是一個狀態管理系統 ❌ 他僅只是一種依賴注入的機制,所以你可以在各個所需的任何值包在 context 裡面,並且通常情況下,您是使用 useState 鉤子或 useReducer 鉤子在 React 元件中管理該狀態的人。
您是一個決定狀態的地方,處理如何更新狀態,然後將值放入 Context 中進行分發。 [name=Kai]
useReducer 再加 useContext 上一種狀態管理系統。而且這相當於 Redux 對 React 的作用,但是 useContext 本身不是狀態管理系統。
Redux v.s. useReudcer
- Redux : 創建了一個全局狀態容器
- useReducer : 在您的元件內創建了一個獨立的元件共置狀態容器
react 內建的 context 與 useReducer hooks ,可以做到 redux 的功能,但我認為 redux 還是有存在的必要的, 例如處理非同步流程時 redux 擁有 redux-thunk 或 redux-saga 等 middleware 可以使用,使用 hooks 的話可能就需要自己處理比較多東西(useEffect) ,還是依照專案的需求決定囉!
參考資料:
- https://zh-hant.reactjs.org/docs/hooks-reference.html
- https://dev.to/milu_franz/demystifying-react-hooks-usereducer-3o3n
- https://dev.to/milu_franz/demystifying-react-hooks-usecontext-5g4a
- https://medium.com/%E6%89%8B%E5%AF%AB%E7%AD%86%E8%A8%98/react-hooks-usestate-vs-usereducer-b14966ad37dd
- https://medium.com/hannah-lin/react-hook-%E7%AD%86%E8%A8%98-usereducer-%E7%9C%9F%E7%9A%84%E8%83%BD%E5%AE%8C%E5%85%A8%E5%8F%96%E4%BB%A3-redux-%E5%97%8E-fabcc1e9b400
教材在此,可以自己去玩玩
Codesandbox 💪