標題有點引戰,但是如果想要跨元件處理狀態,並且狀態非常複雜的話,那麼 useState 可能就不是那麼好管理,也許可以試試看 useReducer。

使用情境

之前有寫過 useState 的介紹文章,裡面的範例是一個點擊器,可以透過點擊不同按鈕去反應結果,這篇就來使用 useReducer 來做出一樣的成果。

初始設置

首先將會分成 3 個元件,分別是加按鈕、減按鈕,還有計算結果,這邊計算結果是用 input 欄位,可以讓使用者自行輸入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 減
const MinusCount = () => {
return <button type="button">-</button>;
};

export default MinusCount;

// 加
const PlusCount = () => {
return <button type="button">+</button>;
};

export default PlusCount;

// 結果
const Result = () => {
return <input type="number" value={0} />;
};

export default Result;

建立預設狀態

1
2
3
const initialState = {
countNumber: 0
}

建立邏輯判斷區來操作狀態

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const reducer = (state, action) => {
switch (action.type) {
case 'PLUS':
return {
...state,
countNumber: state.countNumber + 1
}
case 'MINUS':
return {
...state,
countNumber: state.countNumber - 1
}
case 'UPDATE_NUMBER':
return {
...state,
countNumber: action.payload
}
default:
return state
}
}

使用 useReducer 來帶入狀態以及邏輯判斷

1
const [state, dispatch] = useReducer(reducer, initialState)

這樣大致上就設置好 useReducer 了,接着就可以將狀態以及方法分別給各自的元件。

1
2
3
4
5
<div className='App'>
<MinusCount method={() => dispatch({ type: 'MINUS' })} />
<Result num={state.countNumber} />
<PlusCount method={() => dispatch({ type: 'PLUS' })} />
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// == Result ==
const Result = () => {
const { state, dispatch } = useContext(AppContext)

return (
<input
type='number'
value={state.countNumber}
onChange={(e) =>
dispatch({ type: 'UPDATE_NUMBER', payload: Number(e.target.value) })
}
/>
)
}

// == Plus ==
const PlusCount = ({ method }) => {
const { dispatch } = useContext(AppContext)

return (
<button type='button' onClick={method}>
+
</button>
)
}

// == Minus ==
const MinusCount = ({ method }) => {
const { dispatch } = useContext(AppContext)

return (
<button type='button' onClick={method}>
-
</button>
)
}

搭配使用 useContext

剛剛是使用 props 來傳送,我自己是比較常搭配 useContext 來使用,成果如下 ⬇️

後記

在官方文件中提到 useReducer 是這樣說的:

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

所以如果是比較單純的狀態的話,那麼使用 useState 就可以了,但是我最近是比較常使用 useReducer,因為寫起來蠻像 Redux 的,而且 useReducer 都把邏輯管理在一起,看起來比較容易閱讀,也方便維護(我就做過把之前的 code 給重寫,看起來就很舒服 XD),所以蠻推薦這個 hook👏。