React TypeScript 应用中便捷使用Redux Toolkit方法详解

作者:BokFang 时间:2023-08-11 09:48:21 

前言

本文介绍的主要内容是 Redux-Toolkit 在 React + TypeScript 大型应用中的实践,主要解决的问题是使用 createSlice 的前提下消费 redux 状态仍旧有点繁琐的问题。

阅读本文需要的前置知识:了解 React 、Redux-Toolkit 、TypeScript 的使用。

关于 Redux-Toolkit 提供的各种函数的使用,大家可以去官网 redux-toolkit.js.org/ 学习。

背景

前阵子接到一个任务:在使用 redux 作为状态管理工具的前提下,优化一下消费 redux 的步骤,并制定一套使用规范,让大家在开发这个项目消费 redux 状态时能按照规范来。

说到简化消费 redux 步骤,我第一时间想到的就是 redux 官方推荐的 Redux-Toolkit,于是我就去学习了一下 Redux-Toolkit。

了解完官网和网上各种文章后,我知道了 Redux-Toolkit 在项目中的使用,但是仍然有一个疑问:使用了 createSlice 后,仍然需要在项目的组件中使用 useDispatch 来更新状态,还是有点麻烦。对于组件使用者来说,有没有更方便的方式消费 redux 状态?

在网上逛来逛去,没找到有人发相关的文章,所以我准备自己动手试试看。

Redux-Toolkit 常规使用

我们先来看看目前使用 Redux-Toolkit 消费 redux 状态的方式。举个例子,假设我们现在的业务和蛋糕有关,有两个状态存在 redux,分别是 nameOfCake 和 numOfCakes,我们使用 createSlice 来创建 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>
 );
};

现状的繁琐点:

  • 每次使用 useSelector 来获取 redux 中状态的时候,都需要给 state 加一个 ts 类型 RootState

  • 每次修改 redux 状态时,都需要引入 useDispatch 和一个 updateData,再将调用 updateData 返回的action 给 dispatch

我们的目标就是优化现状的这两个繁琐点。

优化方案

优化 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;

通过简单的封装,我们在业务层就用 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 类型提示或者校验了:

React TypeScript 应用中便捷使用Redux Toolkit方法详解

优化修改 redux 状态的步骤

我们现在想要在业务组件中修改 redux 状态,我们就需要引入 useDispatch 和通过 createSlice 生成的 updateData 函数,再调用 dispatch(updateData(data)) 来更新状态。

能不能优化成,业务组件只需要调用一个 updateData 函数,就可以更新 redux 状态呢?

最简单的方式就是将 dispatch(updateData(data)) 给抽出去:

const useUpdateCakeName = () => {
 const dispatch = useAppDispatch();
 return (payload: InitialState['nameOfCake']) => {
   dispatch(updateCakeName(payload));
 };
};

但是这对于开发者来讲,换汤不换药,还是需要写一个 hook 来 dispatch action。

那我们就来写一个能自动生成 「用于dispatch action 的 updateData 函数」的 hook 吧。先来个 js 版:

const useCakeReduxFunction = (action) => {
 const dispatch = useAppDispatch();
 return (payload) => {
   dispatch(cakeSlice.actions[action](payload));
 };
};

这个倒是好用了一些,业务组件使用起来是这样的:

import { useCakeReduxFunction } from '@/src/store/reducers/cake';
// ...
export const CakeView = () => {
 const updateNum = useCakeReduxFunction('updateCakeNum');
 const updateName = useCakeReduxFunction('updateCakeName');
// ...
 return (
// ...
 );
};

但是还有个问题,这样的话每个 reducer 里都要写一个 useDataReduxFunction,还是不够便捷。我们就自然而然的想到写一个 useCreateReduxFunction 来简化开发流程。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;

在业务方这么使用:

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>
 );
};

这样就达成我们的目标了。

来源:https://juejin.cn/post/7168260892914614309

标签:React,TypeScript,Redux,Toolkit
0
投稿

猜你喜欢

  • SQL子查询全接触

    2007-08-20 10:51:00
  • asp.net php asp jsp 301重定向的代码(集合)

    2023-11-14 15:02:06
  • pyqt5中动画的使用详解

    2023-06-29 19:31:51
  • python正则表达式最详解

    2022-03-13 07:29:47
  • Python 多个图同时在不同窗口显示的实现方法

    2022-07-02 01:20:35
  • 在tensorflow以及keras安装目录查询操作(windows下)

    2023-10-31 10:49:21
  • Python基于Logistic回归建模计算某银行在降低贷款拖欠率的数据示例

    2023-11-27 21:26:53
  • python按比例随机切分数据的实现

    2021-05-28 05:17:34
  • SqlServer 扩展属性的介绍

    2024-01-17 09:32:15
  • Python实现字符串的逆序 C++字符串逆序算法

    2022-04-10 01:35:54
  • 浅谈Python实时检测CPU和GPU的功耗

    2021-06-11 17:05:58
  • python实现requests发送/上传多个文件的示例

    2023-07-23 01:42:39
  • SQL Server 触发器详情

    2024-01-12 23:53:23
  • php封装的单文件(图片)上传类完整实例

    2024-05-03 15:50:18
  • PHP实现的注册,登录及查询用户资料功能API接口示例

    2024-04-30 08:50:18
  • Python中使用md5sum检查目录中相同文件代码分享

    2022-10-31 19:57:59
  • Python限制内存和CPU使用量的方法(Unix系统适用)

    2023-08-02 08:41:44
  • Pytorch可视化的几种实现方法

    2023-06-11 17:44:57
  • 如何在Python中隐藏和加密密码示例详解

    2023-07-19 00:00:09
  • mysql之TIMESTAMP(时间戳)用法详解

    2024-01-13 16:06:42
  • asp之家 网络编程 m.aspxhome.com