Skip to main content

[ react ] useReducer 的使用

useReducer 的使用情境

useReducer 適合較複雜的 state,因為我們可以將狀態的改變統一放在 reducer 去做管理 ,像 useState 的狀態改變就會分散在不同的函式裡面。

當你需要複雜的 state 邏輯而且包括多個子數值或下一個 state 依賴之前的 state,useReducer 會比 useState 更適用。而且 useReducer 可以讓你觸發深層更新的 component 作效能的最佳化,因為你可以傳遞 dispatch 而不是 callback。

useReducer 語法

const [state, dispatch] = useReducer(reducer, initialArg, init?)
  • reducer: 用來設定變更 state 的規則以及如何更新,連接了 state 和 action 之間的關係。 ( The reducer function that specifies how the state gets updated. It must be pure, should take the state and action as arguments, and should return the next state. State and action can be of any types. )

  • initialArg: 設定了 state 初始狀態的值。 ( The value from which the initial state is calculated. It can be a value of any type. How the initial state is calculated from it depends on the next init argument.)

  • init: The initializer function that should return the initial state. If it’s not specified, the initial state is set to initialArg. Otherwise, the initial state is set to the result of calling init(initialArg).

useReducer 主要回傳了兩個值 currentState dispatch function

  • current state : During the first render, it’s set to init(initialArg) or initialArg (if there’s no init).
  • dispatch function : that lets you update the state to a different value and trigger a re-render.

reducer function

定義變更 state 的規則以及如何更新,連接了 state 和 action 之間的關係。

Actions can have any shape. By convention, it’s common to pass objects with a type property identifying the action. It should include the minimal necessary information that the reducer needs to compute the next state.
// 宣告如下
function reducer(state, action) {
// ...
}
// 也可以加入 switch case 的寫法
function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}

case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}

function Form() {
const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });

function handleButtonClick() {
dispatch({ type: 'incremented_age' });
}

function handleInputChange(e) {
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// ...

reducer function 要注意的點

danger

State is read-only. Don’t modify any objects or arrays in state:

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// 🚩 Don't mutate an object in state like this:
state.age = state.age + 1;
return state;
}

Instead, always return new objects from your reducer:

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ✅ Instead, return a new object
return {
...state,
age: state.age + 1
};
}

Form 範例表單

  1. 宣告 reducer function
  2. useReducer api and dispatch an Action
  3. show result and display
// 建立 reducer function 
function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}
import { useReducer } from 'react';

export default function Form() {
// useReducer
const [state, dispatch] = useReducer(reducer, initialState);

function handleButtonClick() {
// dispatch Action
dispatch({ type: 'incremented_age' });
}

function handleInputChange(e) {
// dispatch Action
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// display
return (
<>
<input
value={state.name}
onChange={handleInputChange}
/>
<button onClick={handleButtonClick}>
Increment age
</button>
<p>Hello, {state.name}. You are {state.age}.</p>
</>
);
}

Alt text

資料來源: