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

 更新时间:2022年11月22日 10:44:59   作者:BokFang  
这篇文章主要为大家介绍了React TypeScript 应用中便捷使用Redux Toolkit方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

本文介绍的主要内容是 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 类型提示或者校验了:

优化修改 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>
  );
};

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

总结

核心代码如下:

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

经过上面的优化方案,我们封装了 useAppSelector 、 useAppDispatch 和 useCreateReduxFunction三个 hook,解决了目前使用 Redux-Toolkit 还会存在的一些繁琐点。

以上就是React TypeScript 应用中便捷使用Redux Toolkit方法详解的详细内容,更多关于React TypeScript使用Redux Toolkit的资料请关注脚本之家其它相关文章!

相关文章

  • React如何避免子组件无效刷新

    React如何避免子组件无效刷新

    这篇文章主要介绍了React几种避免子组件无效刷新的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • react的hooks的用法详解

    react的hooks的用法详解

    这篇文章主要介绍了react的hooks的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • 如何强制刷新react hooks组件

    如何强制刷新react hooks组件

    这篇文章主要介绍了如何强制刷新react hooks组件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • React 中使用 Redux 的 4 种写法小结

    React 中使用 Redux 的 4 种写法小结

    这篇文章主要介绍了在 React 中使用 Redux 的 4 种写法,Redux 一般来说并不是必须的,只有在项目比较复杂的时候,比如多个分散在不同地方的组件使用同一个状态,本文就React使用 Redux的相关知识给大家介绍的非常详细,需要的朋友参考下吧
    2022-06-06
  • React事件机制源码解析

    React事件机制源码解析

    这篇文章主要介绍了React事件机制源码解析的相关资料,帮助大家更好的理解和学习使用React框架,感兴趣的朋友可以了解下
    2021-04-04
  • react hooks入门详细教程

    react hooks入门详细教程

    这篇文章主要介绍了react hooks入门详细教程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • 在react项目中使用antd的form组件,动态设置input框的值

    在react项目中使用antd的form组件,动态设置input框的值

    这篇文章主要介绍了在react项目中使用antd的form组件,动态设置input框的值,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • 使用React实现一个简单的待办任务列表

    使用React实现一个简单的待办任务列表

    这篇文章主要给大家介绍了使用React和Ant Design库构建的待办任务列表应用,它包含了可编辑的表格,用户可以添加、编辑和完成任务,以及保存任务列表数据到本地存储,文中有相关的代码示例,需要的朋友可以参考下
    2023-08-08
  • React实现全选功能

    React实现全选功能

    这篇文章主要为大家详细介绍了React实现全选功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • React通过hook实现封装表格常用功能

    React通过hook实现封装表格常用功能

    这篇文章主要为大家详细介绍了React通过hook封装表格常用功能的使用,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考下
    2023-12-12

最新评论