useReducer
是 useState
的一個替代方案,用於處理比較複雜的狀態,或是裡面有多個屬性的 state
,類似於 redux 中的 reducer
,不同的地方在於它多了兩個參數 reducer
、initialAction
。
const [state, setState] = useState(initialState)
const [state, dispatch] = useReducer(reducer, initialState, initialAction)
state
就是資料的狀態。dispatch
是更新 state
的方法,跟 setState
的差別在於它接受 action
作為參數。reducer
是一個函式,用於處理 action
的描述,並以此更新 state
。它接受兩個參數, state
(舊state) 和 action
,並返回一個新的 state
,如下面的結構(state, action) => newState
action
是一個物件,用於描述 state
更新的方式,它通常有一個 type
屬性,也可以攜帶其他參數,下面是一個 action
的範例,action
只能透過 dispatch
來調用,dispatch
會將 action
傳給 reducer
。const action = {
type: 'increment', // increment 表示 state 的值要增加
payload: {
other: 'value' // 其他的參數
}
}
initialState
就是初始的 state
。initialAction
是 useReducer
第一次被執行時的 action
。下面是一個簡易的 reducer
使用範例,我們利用三個 button
來使用不同的 action
type
,這樣當 dispatch
調用 action
時,觸發 reducer
,就能起到不同的效果。
import { useReducer } from 'react';
//初始值為 0
const initialState = { count: 0 }
// 接受 state, action 參數
const reducer = (state, action) => {
// 根據 action 的 type 來更新 state
switch (action.type) {
// type 為 reset,就重新轉為初始值
case 'reset':
return initialState
// type 為 increment,就 +1
case 'increment':
return {count: state.count + 1}
// type 為 decrement,就 -1
case 'decrement':
return {count: state.count - 1}
// 當 type 不屬於上面任何值,就返回當前的 state
default:
return state
}
}
const TestComponent = () => {
const [state, dispatch] = useReducer(reducer, initialState)
// 透過 dispatch 調用 action,來判斷更新的方式
return <div>
<p>current count {state.count}</p>
<div><button onClick={() => dispatch({type: 'reset'})}>reset</button></div>
<div><button onClick={() => dispatch({type: 'increment'})}>+1</button></div>
<div><button onClick={() => dispatch({type: 'decrement'})}>-1</button></div>
</div>
}
上一篇有提到我們可以利用 useContext
做到 state
的全域調用,我們也可以搭配 useContext
來使用 useReducer
以達到較為複雜的 state
管理及更新。下面我們先建立初始值和 Context
const userInitState = {
name: 'Leo',
age: 29
}
export const UserContext = React.createContext({
userInitState
});
接著我們建立更新 state
用的 reducer
const userReducer = (state, action) => {
switch (action.type) {
case 'reset':
return userInitState
case 'changeName':
return {name: 'Jack', age: userInitState.age}
case 'ChangeAge':
return {name: userInitState.name ,age: 25}
default:
return state
}
}
我們接著在 UserProfile 中去調用 useReducer
,並將 userState
和 userDispatch
透過 provider
傳送到 TestComponent1
及 TestComponent2元件
const UserProfile = () => {
const [userState, userDispatch] = useReducer(userReducer, userInitState);
return <div>
<UserContext.Provider
value={{
userState,
userDispatch
}}
>
<TestComponent1 />
<TestComponent2 />
</UserContext.Provider>
</div>
}
這樣我們就能在 TestComponent1
及 TestComponent2元件
中調用 userDispatch
並同步更新 userState
,達到管理全域狀態並更新的效果。
const TestComponent1 = (props) => {
const { userState, userDispatch } = React.useContext(UserContext);
// 調用 userDispatch
return <div>
<button onClick={() => userDispatch({type: 'changeName'})}>change Name</button>
<button onClick={() => userDispatch({type: 'ChangeAge'})}>change Age</button>
<button onClick={() => userDispatch({type: 'reset'})}>reset data</button>
</div>
}
const TestComponent2 = (props) => {
const { userState, userDispatch } = React.useContext(UserContext);
// 同步更新 userState
return <div>
<div>name: {userState.name}</div>
<div>age: {userState.age}</div>
</div>
}