一連幾天都在介紹 memo
、useCallback
、useMemo
今天我們就來換個口味來介紹 useReducer
,看看他能拿來做什麼?
一如既往,先來看看官方對於 useReducer
給的定義
An alternative to
useState
. Accepts a reducer of type(state, action) => newState
, and returns the current state paired with adispatch
method.
(If you’re familiar with Redux, you already know how this works.)
useReducer
is usually preferable touseState
when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.useReducer
also lets you optimize performance for components that trigger deep updates because
you can passdispatch
down instead of callbacks.
文件中提到了,useReducer
是可作爲一個useState
替代方案,而且可以管理更複雜的邏輯,以及狀態
以我們在使用 useState
的時候,我通常都只會定義一個狀態,若要管理以及定義多個狀態,我們會使用更多的 useState
,並且使用一個function
去包裝我們要的邏輯,簡單舉個例子
這邊我們建立兩個數字的狀態,並且做出三個 function
,去包裝我們想要達成的邏輯~
// App.js
import { useState } from "react"
const App = () => {
const [count, setCount] = useState(0)
const [secondCount, setSecondCount] = useState(10)
const addHandler = () => {
setCount(perv => perv + 1)
setSecondCount(perv => perv + 2)
}
const minusHandler = () => {
setCount(perv => perv - 1)
setSecondCount(perv => perv - 2)
}
const resetHandler = () => {
setCount(0)
setSecondCount(10)
}
return (
<div>
<div>
第一個數字: {count}
</div>
<button onClick={addHandler}>點我 + </button>
<button onClick={resetHandler}>重置</button>
<button onClick={minusHandler}>點我 - </button>
<div>
第二個數字: {secondCount}
</div>
</div>
)
}
export default App
那接下來我們來看看 useReducer
是怎麼寫的,並且可以達到一樣的效果
// App.js
import { useReducer } from 'react'
const initialState = { count: 0, secondCount: 10 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1,
secondCount: state.secondCount + 1,
};
case 'decrement':
return {
count: state.count - 1,
secondCount: state.secondCount - 2,
};
case 'reset':
return initialState
default:
throw new Error();
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<div>
第一個數字: {state.count}
</div>
<button onClick={() => dispatch({type: 'decrement'})}>點我 + </button>
<button onClick={() => dispatch({type: 'increment'})}>點我 - </button>
<button onClick={() => dispatch({type: 'reset'})}>重置</button>
<div>
第二個數字: {state.secondCount}
</div>
</div>
);
}
export default App;
一開始看到會覺得很複雜,但是讓我們來一一拆解吧
const [state, dispatch] = useReducer(reducer, initialState);
我們先看到 state
以及 initialState
useReducer
第二個參數,顧名思義它就是起始的狀態,通常我們會給他一個物件,畢竟我們是要改變以及管理狀態
所以這邊我們定義 initialState
為這樣
const initialState = { count: 0, secondCount: 10 }
然後我們將state
在畫面上印出來,會得到下面這樣
有沒有很像我們之前很常使用的 useState
,設定一個常數,並且給他初始值
再來就是我們要如何去改變狀態
再來回頭看第useReduce
一個參數看起,他會接受一個 function
在這邊我們定義為 reducer
function
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1,
secondCount: state.secondCount + 1,
};
case 'decrement':
return {
count: state.count - 1,
secondCount: state.secondCount - 2,
};
case 'reset':
return initialState
default:
throw new Error();
}
}
先看到這裡 reducer(state, action)
action
會被 dispatch
呼叫,而呼叫的條件則是我們定義的,這邊我們將他定義為 type
是 increment
、decrement
、reset
時,分別會去做什麼事,像是下面我們所呼叫的樣子
<button onClick={() => dispatch({type: 'decrement'})}>點我 - </button>
<button onClick={() => dispatch({type: 'increment'})}>點我 + </button>
<button onClick={() => dispatch({type: 'reset'})}>重置</button>
而這裡的state
則是剛剛陣列中的 state
雖然這樣看起來有點複雜,但是如果將 useReducer
用熟之後,你會發現它可以做到 useState
做不到的事,明天我們將為介紹 useReducer
+ useContenxt
的組合技~
今天介紹了 useReducer
起初我看到他時我也是看得一頭霧水,但是只要熟悉之後他就是很強的武器了,明天會繼續介紹 useReducer
的更多用法