React?TypeScript?應用中便捷使用Redux?Toolkit方法詳解
前言
本文介紹的主要內(nèi)容是 Redux-Toolkit 在 React + TypeScript 大型應用中的實踐,主要解決的問題是使用 createSlice 的前提下消費 redux 狀態(tài)仍舊有點繁瑣的問題。
閱讀本文需要的前置知識:了解 React 、Redux-Toolkit 、TypeScript 的使用。
關于 Redux-Toolkit 提供的各種函數(shù)的使用,大家可以去官網(wǎng) redux-toolkit.js.org/ 學習。
背景
前陣子接到一個任務:在使用 redux 作為狀態(tài)管理工具的前提下,優(yōu)化一下消費 redux 的步驟,并制定一套使用規(guī)范,讓大家在開發(fā)這個項目消費 redux 狀態(tài)時能按照規(guī)范來。
說到簡化消費 redux 步驟,我第一時間想到的就是 redux 官方推薦的 Redux-Toolkit,于是我就去學習了一下 Redux-Toolkit。
了解完官網(wǎng)和網(wǎng)上各種文章后,我知道了 Redux-Toolkit 在項目中的使用,但是仍然有一個疑問:使用了 createSlice 后,仍然需要在項目的組件中使用 useDispatch 來更新狀態(tài),還是有點麻煩。對于組件使用者來說,有沒有更方便的方式消費 redux 狀態(tài)?
在網(wǎng)上逛來逛去,沒找到有人發(fā)相關的文章,所以我準備自己動手試試看。
Redux-Toolkit 常規(guī)使用
我們先來看看目前使用 Redux-Toolkit 消費 redux 狀態(tài)的方式。舉個例子,假設我們現(xiàn)在的業(yè)務和蛋糕有關,有兩個狀態(tài)存在 redux,分別是 nameOfCake 和 numOfCakes,我們使用 createSlice 來創(chuàng)建 reducer 和 actions:
// 文件位置: app/src/store/reducers/cake.ts import { createSlice, PayloadAction } from '@reduxjs/toolkit'; type InitialState = { numOfCakes: number; nameOfCake: string; }; const initialState: InitialState = { numOfCakes: 20, nameOfCake: 'great cake', }; const cakeSlice = createSlice({ name: 'cake', initialState, reducers: { updateCakeNum: (state, action: PayloadAction<number>) => { state.numOfCakes = action.payload; }, updateCakeName: (state, action: PayloadAction<string>) => { state.nameOfCake = action.payload; }, }, }); export default cakeSlice.reducer; export const { updateCakeNum, updateCakeName } = cakeSlice.actions;
接著我們在組件中消費:
// 文件位置: app/src/pages/Cake/components/CakeView/index.tsx import { RootState } from '@/src/store'; import { useSelector, useDispatch } from 'react-redux'; import { useRecordReduxFunction, updateCakeNum, updateCakeName, } from '@/src/store/reducers/cake'; export const CakeView = () => { const numOfCakes = useSelector((state: RootState) => state.cake.numOfCakes); const nameOfCake = useSelector((state: RootState) => state.cake.nameOfCake); const dispatch = useDispatch(); const updateNum = () => { dispatch(updateCakeNum(100)); }; const updateName = () => { dispatch(updateCakeName('best cake')); }; return ( <div> <h3>Number of cakes - {numOfCakes}</h3> <h3>Name of cakes - {nameOfCake}</h3> <button onClick={updateName}>change cake's name</button> <button onClick={updateNum}>change cake's number</button> </div> ); };
現(xiàn)狀的繁瑣點:
- 每次使用 useSelector 來獲取 redux 中狀態(tài)的時候,都需要給 state 加一個 ts 類型 RootState
- 每次修改 redux 狀態(tài)時,都需要引入 useDispatch 和一個 updateData,再將調(diào)用 updateData 返回的action 給 dispatch
我們的目標就是優(yōu)化現(xiàn)狀的這兩個繁瑣點。
優(yōu)化方案
優(yōu)化 useDispatch 和 useSelector
對于繁瑣點1,我們可以對 useDispatch 和 useSelector 進行簡單的封裝,增加 ts 類型校驗。代碼如下:
// 文件位置: app/src/hooks/useReduxHook.ts import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; import type { RootState, AppDispatch } from '../store'; export const useAppDispatch = () => useDispatch<AppDispatch>(); export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
其中 RootState 和 AppDispatch 在 store 中導出:
import { configureStore } from '@reduxjs/toolkit'; import cakeReducer from './reducers/cake'; const store = configureStore({ reducer: { cake: cakeReducer, } }) export default store; export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch;
通過簡單的封裝,我們在業(yè)務層就用 useAppSelector 和 useAppDispatch 代替 useSelector 和 useDispatch:
import { useAppSelector, useAppDispatch } from '@src/hooks/useReduxHook'; // ... export const CakeView = () => { const numOfCakes = useAppSelector((state) => state.cake.numOfCakes); const nameOfCake = useAppSelector((state) => state.cake.nameOfCake); const dispatch = useAppDispatch(); // ... return ( // ... ); };
可以看到這樣就有 ts 類型提示或者校驗了:
優(yōu)化修改 redux 狀態(tài)的步驟
我們現(xiàn)在想要在業(yè)務組件中修改 redux 狀態(tài),我們就需要引入 useDispatch 和通過 createSlice 生成的 updateData 函數(shù),再調(diào)用 dispatch(updateData(data))
來更新狀態(tài)。
能不能優(yōu)化成,業(yè)務組件只需要調(diào)用一個 updateData 函數(shù),就可以更新 redux 狀態(tài)呢?
最簡單的方式就是將 dispatch(updateData(data))
給抽出去:
const useUpdateCakeName = () => { const dispatch = useAppDispatch(); return (payload: InitialState['nameOfCake']) => { dispatch(updateCakeName(payload)); }; };
但是這對于開發(fā)者來講,換湯不換藥,還是需要寫一個 hook 來 dispatch action。
那我們就來寫一個能自動生成 「用于dispatch action 的 updateData 函數(shù)」的 hook 吧。先來個 js 版:
const useCakeReduxFunction = (action) => { const dispatch = useAppDispatch(); return (payload) => { dispatch(cakeSlice.actions[action](payload)); }; };
這個倒是好用了一些,業(yè)務組件使用起來是這樣的:
import { useCakeReduxFunction } from '@/src/store/reducers/cake'; // ... export const CakeView = () => { const updateNum = useCakeReduxFunction('updateCakeNum'); const updateName = useCakeReduxFunction('updateCakeName'); // ... return ( // ... ); };
但是還有個問題,這樣的話每個 reducer 里都要寫一個 useDataReduxFunction,還是不夠便捷。我們就自然而然的想到寫一個 useCreateReduxFunction 來簡化開發(fā)流程。useCreateReduxFunction 的代碼如下;
type GetArrFirst<T> = T extends [infer Res, ...infer P] ? Res : unknown; export const useCreateReduxFunction = <S extends Slice>(slice: S) => { return <T extends keyof S['actions']>(name: T) => { const dispatch = useAppDispatch(); const actionCreator = (slice.actions as S['actions'])[name]; return (payload: GetArrFirst<Parameters<typeof actionCreator>>) => { dispatch(actionCreator(payload)); }; }; };
這樣就好辦了,我們在 reducer 中這樣使用:
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { useCreateReduxFunction } from '../../app/hooks'; type InitialState = { numOfCakes: number; cakeName: string; }; const initialState: InitialState = { numOfCakes: 20, cakeName: 'great cake', }; const cakeSlice = createSlice({ name: 'cake', initialState, reducers: { updateCakeNum: (state, action: PayloadAction<number>) => { state.numOfCakes = action.payload; }, updateCakeName: (state, action: PayloadAction<string>) => { state.cakeName = action.payload; }, }, }); export const useCakeReduxFunction = useCreateReduxFunction(cakeSlice); export default cakeSlice.reducer; export const { updateCakeNum, updateCakeName } = cakeSlice.actions;
在業(yè)務方這么使用:
import { useAppSelector } from '@src/hooks/useReduxHook'; import { useCakeReduxFunction } from '@src/store/reducers/cake'; export const CakeView = () => { const numOfCakes = useAppSelector((state) => state.cake.numOfCakes); const nameOfCake = useAppSelector((state) => state.cake.cakeName); const updateNum = useCakeReduxFunction('updateCakeNum'); const updateName = useCakeReduxFunction('updateCakeName'); return ( <div> <h3>Number of cakes - {numOfCakes}</h3> <h3>Name of cakes - {nameOfCake}</h3> <button onClick={() => {updateName('best cake')}}>change cake's name</button> <button onClick={() => {updateNum(100)}}>change cake's number</button> </div> ); };
這樣就達成我們的目標了。
總結
核心代碼如下:
type GetArrFirst<T> = T extends [infer Res, ...infer P] ? Res : unknown; export const useAppDispatch = () => useDispatch<AppDispatch>(); export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; export const useCreateReduxFunction = <S extends Slice>(slice: S) => { return <T extends keyof S['actions']>(name: T) => { const dispatch = useAppDispatch(); const actionCreator = (slice.actions as S['actions'])[name]; return (payload: GetArrFirst<Parameters<typeof actionCreator>>) => { dispatch(actionCreator(payload)); }; }; };
經(jīng)過上面的優(yōu)化方案,我們封裝了 useAppSelector 、 useAppDispatch 和 useCreateReduxFunction三個 hook,解決了目前使用 Redux-Toolkit 還會存在的一些繁瑣點。
以上就是React TypeScript 應用中便捷使用Redux Toolkit方法詳解的詳細內(nèi)容,更多關于React TypeScript使用Redux Toolkit的資料請關注腳本之家其它相關文章!
相關文章
react-router-dom6(對比?router5)快速入門指南
這篇文章主要介紹了快速上手react-router-dom6(對比?router5),通過本文學習最新的react-router-dom?v6版本的路由知識,并且會與v5老版本進行一些對比,需要的朋友可以參考下2022-08-08基于React Native 0.52實現(xiàn)輪播圖效果
這篇文章主要為大家詳細介紹了基于React Native 0.52實現(xiàn)輪播圖效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11React為什么需要Scheduler調(diào)度器原理詳解
這篇文章主要為大家介紹了React為什么需要Scheduler調(diào)度器原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10在react-router4中進行代碼拆分的方法(基于webpack)
這篇文章主要介紹了在react-router4中進行代碼拆分的方法(基于webpack),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03React?Hooks之useDeferredValue鉤子用法示例詳解
useDeferredValue鉤子的主要目的是在React的并發(fā)模式中提供更流暢的用戶體驗,特別是在有高優(yōu)先級和低優(yōu)先級更新的情況下,本文主要講解一些常見的使用場景及其示例2023-09-09基于Cloud?Studio構建React完成點餐H5頁面(騰訊云?Cloud?Studio?實戰(zhàn)訓練營)
最近也是有機會參與到了騰訊云舉辦的騰訊云Cloud Studio實戰(zhàn)訓練營,借此了解了騰訊云Cloud?Studio產(chǎn)品,下面就來使用騰訊云Cloud?Studio做一個實戰(zhàn)案例來深入了解該產(chǎn)品的優(yōu)越性吧,感興趣的朋友跟隨小編一起看看吧2023-08-08