一文帶你搞懂React中的useReducer
useReducer 是除useState之外另一個與狀態(tài)管理相關的 hook,對于熟悉 Redux 的工程師而言,理解 useReducer 將變得簡單,在 React 內部,useState 由 useReducer 實現。
useReducer 的類型定義
useReducer 的類型定義如下:
function useReducer<R extends ReducerWithoutAction<any>, I>( reducer: R, initializerArg: I, initializer: (arg: I) => ReducerStateWithoutAction<R> ): [ReducerStateWithoutAction<R>, DispatchWithoutAction]; function useReducer<R extends ReducerWithoutAction<any>>( reducer: R, initializerArg: ReducerStateWithoutAction<R>, initializer?: undefined ): [ReducerStateWithoutAction<R>, DispatchWithoutAction]; function useReducer<R extends Reducer<any, any>, I>( reducer: R, initializerArg: I & ReducerState<R>, initializer: (arg: I & ReducerState<R>) => ReducerState<R> ): [ReducerState<R>, Dispatch<ReducerAction<R>>]; function useReducer<R extends Reducer<any, any>, I>( reducer: R, initializerArg: I, initializer: (arg: I) => ReducerState<R> ): [ReducerState<R>, Dispatch<ReducerAction<R>>]; function useReducer<R extends Reducer<any, any>>( reducer: R, initialState: ReducerState<R>, initializer?: undefined ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
useReducer 的類型定義很復雜,一共有 5 個重載,總體而言,它最多接受3個參數,第 1 個參數是一個用于更新狀態(tài)的函數,之后將它稱為 reducer。第 3 個參數非必填,如果不存在第 3 個參數,那么第 2 個參數將作為狀態(tài)的初始值;如果存在第 3 個參數,那么它必須是函數,此時第 2 個參數被傳遞給該函數用于計算狀態(tài)的初始值,該函數只在組件初始渲染時執(zhí)行一次。useReducer 的返回值是一個長度為 2 的數組,數組的第 1 個位置是狀態(tài)值,第 2 個位置是一個用于觸發(fā)狀態(tài)更新的函數,將它記為dispatch,調用 dispatch 將導致 reducer 被調用。接下來通過計數器 demo 對比 useState 和useReducer 用法上的差異。
用 useState 實現計數器
用 useState 實現計數器,代碼如下:
function UseStateCounterDemo() { const [value, setValue] = useState<number>(0) const [step, setStep] = useState<number>(1) const onChangeStep = (ev: React.ChangeEvent<HTMLInputElement>) => { setStep(Number(ev.target.value)) } return ( <div> <h2>用useState實現計數器</h2> count: {count}; step: <input type='number' value={step} onChange={onChangeStep}/> <button onClick={() => setValue(value + step)}>加</button> <button onClick={() => setValue(value - step)}>減</button> <button onClick={() => {setValue(0); setStep(1)}}>重置</button> </div> ) }
上述代碼很簡單,它使用 useState 定義了兩個狀態(tài),分別表示計數器的值和加減步數,在過去的文章里已對 useState 做過詳細的介紹,這里不再贅述,下面詳細介紹用 useReducer 實現計數器。
用 useReducer 實現計數器
定義 reducer
使用 useReducer hook 離不開reducer,它是一個函數,用于更新 useReducer 返回的狀態(tài),代碼如下:
const initArg: Counter = {value: 0, step: 1} // 它用于更新state function reducer(prevState: Counter, action: Action): Counter { switch (action.type) { case 'increment': return { ...prevState, value: prevState.value + prevState.step } case 'decrement': return { ...prevState, value: prevState.value - prevState.step } case 'reset': return initArg case 'changeStep': return { ...prevState, step: action.value || initArg.value } default: throw new Error(); } }
reducer 用于更新狀態(tài),計數器 demo 有兩個狀態(tài),分別是計數器的當前值和它的加減步數,這兩個狀態(tài)密切相關,這里用一個 TS 接口去描述它,代碼如下:
interface Counter { // 計數器的當前值 value: number // 計數器的加減步數 step: number }
計數器有 3 種操作,分別是加、減、重置和修改步數,這里用 TS 接口描述這些行為,代碼如下:
interface Action { type: 'increment' | 'decrement' | 'reset'|'changeStep', value?: number }
在組件中使用 useReducer
// 計數器的初始值 const initArg: Counter = {value: 0, step: 1} function UseReducerCounterDemo() { // 使用上一步定義 reducer const [counter, dispatch] = useReducer(reducer, initArg) const onChangeStep = (ev: React.ChangeEvent<HTMLInputElement>) => { dispatch({type: 'changeStep',value: Number(ev.target.value)}) } return ( <div> <h2>用useReducer實現計數器</h2> count: {counter.value}; step: <input type='number' value={counter.step} onChange={onChangeStep}/> <button onClick={() => dispatch({type: 'increment'})}>加</button> <button onClick={() => dispatch({type: 'decrement'})}>減</button> <button onClick={() => dispatch({type: 'reset'})}>重置</button> </div> ) }
只考慮代碼量,讀者應該都會認為useState比useReducer更簡潔。仔細觀察可以發(fā)現,上述計數器除了有value還有step,step對value有影響,UseStateCounterDemo組件將它們零散地保存在不同的狀態(tài)中,UseStateCounterDemo組件將它們關聯在同一個狀態(tài)中,內聚性更高。useState與useReducer沒有優(yōu)劣之外,它們有各自適用的場景,這里有如下建議:
1.當狀態(tài)是一個擁有很多屬性的復雜對象,并且狀態(tài)更新涉及復雜的邏輯時,推薦使用useReducer。
2.當某個狀態(tài)的更新受另一個狀態(tài)影響時,推薦使用 useReducer將它們放在一起。
3.當狀態(tài)只是單獨的基本數據類型時,推薦使用 useState。
在介紹 useEffect時曾強調,為了在 effect 中拿到狀態(tài)最新的值,必須給 effect 設置正確地依賴項。在 useEffect 中使用 useReducer 返回的 dispatch 能讓 effect 自給自足,減少依賴項。示例代碼如下:
function DispatchDemo(props: {step: number}) { function reducer(value: number) { // 始終能訪問到最新的 step return props.step + value } const [value, dispatch] = useReducer(reducer, 0) useEffect(() => { document.body.addEventListener('click', dispatch) return () => { document.body.removeEventListener('click', dispatch) } }, []) return // todo }
上述代碼 useEffect 的第二個參數為空數組,這意味著 effect 只在組件初始渲染時執(zhí)行。由于React 會讓 dispatch 在組件的每次渲染中保持唯一的引用,所以 dispatch 不必出現在effect的依賴中,此特性與 ref 類似。雖然 dispatch 的引用保持不變,但它能調用組件本次渲染時的reducer,在 reducer 作用域將得到最新的 state 和 props。
推薦閱讀:
我搞懂了 React 的 useState 和 useEffect
到此這篇關于一文帶你搞懂React中的useReducer的文章就介紹到這了,更多相關React useReducer內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解create-react-app 2.0版本如何啟用裝飾器語法
這篇文章主要介紹了詳解create-react-app 2.0版本如何啟用裝飾器語法,cra2.0時代如何啟用裝飾器語法呢? 我們依舊采用的是react-app-rewired, 通過劫持webpack cofig對象, 達到修改的目的2018-10-10React?Hooks之usePolymerAction抽象代碼結構設計理念
這篇文章主要為大家介紹了React?Hooks之usePolymerAction抽象代碼結構設計理念,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09手挽手帶你學React之React-router4.x的使用
這篇文章主要介紹了手挽手帶你學React之React-router4.x的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-02-02