Redux-ReduxToolkit

603 阅读8分钟

Redux Toolkit的核心API

  1. configureStore:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。
    • 包装createStore,同时提供简化的配置选项和良好的默认值
    • 自动组合单独的slice reducer
    • 可以添加任何中间件
      • 默认包含redux-thunk,并启用Redux-DevTools调试工具

configureStore用于创建store对象,常见参数如下:

  • reducer: 将slice中的reducer可以组成一个对象传入此处;
  • middleware:可以使用参数,传入其他的中间件(自行了解);
  • devTools:是否配置devTools工具,默认为true;
import { configureStore } from "@reduxjs/toolkit"

import counterReducer from "./features/counter"
import homeReducer from "./features/home"

const store = configureStore({
  reducer: {
    counter: counterReducer,
    home: homeReducer
  }
})

export default store
  1. createSlice:接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions
    • 接受一个具有render函数的对象
    • 可以配置切片名称
    • 初始状态值
    • 自动 生成切片,并带有相应的actions
  • reducer进行重构: 通过createSlice创建一个slice
  • createSlice主要包含如下几个参数:
  • name:用户标记 slice 的名词
    • 在之后的redux-devtool中会显示对应的名词;
  • initialState:初始化值
    • 第一次初始化时的值;
  • reducers :相当于之前的 reducer 函数
    • 对象类型,并且可以添加很多的函数;
    • 函数类似于redux原来reducer中的一个case语句;
    • 函数的参数:
      • 参数一:state
      • 参数二:调用这个action时,传递的action参数;
  • createSlice 返回值是一个对象,包含所有的 actions
import { createSlice } from "@reduxjs/toolkit"
// 创建一个名为 "counter" 的切片
const counterSlice = createSlice({
  name: "counter",
  initialState: {
    counter: 888
  },
  reducers: {
    addNumber(state, { payload }) {
      state.counter = state.counter + payload
    },
    subNumber(state, { payload }) {
      state.counter = state.counter - payload
    }
  }
})

// 导出生成的 action creators
export const { addNumber, subNumber } = counterSlice.actions
// 导出生成的 reducer 作为默认导出
export default counterSlice.reducer
  1. createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分派动作类型的 thunk
    • 接受一个动作类型字符和一个返回承诺的函数
    • 生成一个pending/fulfilled/rejected基于该承诺分配动作类型的Thunk

Redux Toolkit的异步操作

之前通过redux-thunk中间件让dispatch中可以进行异步操作

  1. Redux Toolkit默认已经给我们继承了Thunk相关的功能:createAsyncThunk
export const fetchHomeMultidataAction = createAsyncThunk(
  "fetch/homemultidata", 
  async (extraInfo, { dispatch, getState }) => {
    // console.log(extraInfo, dispatch, getState)
    // 1.发送网络请求, 获取数据
    const res = await axios.get("http://123.207.32.32:8000/home/multidata")

    // 2.取出数据, 并且在此处直接dispatch操作(可以不做)
    const banners = res.data.data.banner.list
    const recommends = res.data.data.recommend.list
    dispatch(changeBanners(banners))
    dispatch(changeRecommends(recommends))

    // 3.返回结果, 那么action状态会变成fulfilled状态
    return res.data
})
  1. createAsyncThunk 创建出来的 actiondispatch 时,会存在三种状态:
    • pendingaction 被发出,但是还没有最终的结果;
    • fulfilled:获取到最终的结果(有返回值的结果);
    • rejected:执行过程中有错误或者抛出了异常;
  • 我们可以在 createSliceentraReducer 中监听这些结果:

在 Redux Toolkit 中,我们可以使用 extraReducers 属性来定义异步操作的状态更新逻辑。这里的异步操作是通过使用异步中间件(如 Redux Thunk 或 Redux Saga)来派发的,例如通过 fetchHomeMultidataAction action。

extraReducers: {
  // 处理 fetchHomeMultidataAction 的 pending(异步操作进行中)状态
  [fetchHomeMultidataAction.pending](state, action) {
    console.log("fetchHomeMultidataAction pending")
  },
  
  // 处理 fetchHomeMultidataAction 的 fulfilled(异步操作成功完成)状态
  [fetchHomeMultidataAction.fulfilled](state, { payload }) {
    // 更新状态中的 banners 和 recommends 属性
    state.banners = payload.data.banner.list
    state.recommends = payload.data.recommend.list
  },
  
  // 处理 fetchHomeMultidataAction 的 rejected(异步操作被拒绝/失败)状态
  [fetchHomeMultidataAction.rejected](state, action) {
    console.log("fetchHomeMultidataAction rejected")
  }
}

extraReducers 中,我们使用 action 的类型作为键来定义对应的状态更新逻辑。在这里,fetchHomeMultidataAction 表示一个异步操作的 action。使用 [fetchHomeMultidataAction.pending][fetchHomeMultidataAction.fulfilled][fetchHomeMultidataAction.rejected] 来访问对应异步操作的不同状态。

  • pending:处理异步操作进行中的状态,例如显示加载指示器。
  • fulfilled:处理异步操作成功完成的状态,从 payload 中提取数据,并更新状态中的相应属性。
  • rejected:处理异步操作被拒绝/失败的状态,例如显示错误消息。

在对应的状态更新函数中,可以执行各种逻辑,例如更新状态属性、记录日志、触发其他 action 等。

通过使用 extraReducers,我们可以在 Redux Toolkit 中更方便地处理异步操作的状态更新,而无需手动编写大量的 case 语句。这简化了异步操作的处理,并提供了一种结构化的方式来管理不同的异步状态。

  1. extraReducer 的另外一种写法
  • extraReducer 还可以传入一个函数,函数接受一个 builder 参数。
    • 我们可以向 builder 中添加 case 来监听异步操作的结果:

代码块展示了使用 extraReducers 的另一种语法,使用了 Redux Toolkit 中的 builder 对象来定义状态更新逻辑。

extraReducers: (builder) => {
  // 处理 fetchHomeMultidataAction 的 pending(异步操作进行中)状态
  builder.addCase(fetchHomeMultidataAction.pending, (state, action) => {
    console.log("fetchHomeMultidataAction pending")
  });
  
  // 处理 fetchHomeMultidataAction 的 fulfilled(异步操作成功完成)状态
  builder.addCase(fetchHomeMultidataAction.fulfilled, (state, { payload }) => {
    // 更新状态中的 banners 和 recommends 属性
    state.banners = payload.data.banner.list
    state.recommends = payload.data.recommend.list
  });
}

在这种语法中,我们使用了 builder 对象来定义状态更新逻辑。通过调用 addCase 方法,我们可以为特定的 action 类型添加状态更新函数。

  • addCase:用于添加状态更新函数。第一个参数是 action 类型,可以是一个 action 或一个带有 type 属性的对象。第二个参数是一个回调函数,用于定义状态更新逻辑。回调函数接收两个参数:state 表示当前的状态,action 表示派发的 action。

在回调函数中,我们可以执行各种逻辑,例如更新状态属性、记录日志、触发其他 action 等。

通过使用 extraReducersbuilder 对象,我们可以更清晰地定义状态更新逻辑,并且可以链式调用 addCase 方法来添加多个状态更新函数。

这种语法是 Redux Toolkit 提供的一种更简洁和可读性更高的方式来定义状态更新逻辑,并且与 createSlice 函数配合使用时非常方便。

Redux Toolkit的数据不可变性(了解)

  1. 在React开发中,我们总是会强调数据的不可变性:
    • 无论是类组件中的state,还是redux中管理的state;
    • 事实上在整个JavaScript编码过程中,数据的不可变性都是非常重要的;
  2. 所以在前面我们经常会进行浅拷贝来完成某些操作,但是浅拷贝事实上也是存在问题的:
    • 比如过大的对象,进行浅拷贝也会造成性能的浪费;
    • 比如浅拷贝后的对象,在深层改变时,依然会对之前的对象产生影响;
  3. 事实上Redux Toolkit底层使用了 immerjs 的一个库来保证数据的不可变性。
  4. 讲解 immutable-js 库的底层原理和使用方法: mp.weixin.qq.com/s/hfeCDCcod…
  5. 为了节约内存,又出现了一个新的算法:Persistent Data Structure(持久化数据结构或一致性数据结构);
    • 用一种数据结构来保存数据;
    • 当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费;

React中的state如何管理

  1. 三种状态管理方式:

    • 方式一:组件中自己的 state 管理;
    • 方式二:Context 数据的共享状态;
    • 方式三:Redux 管理应用状态;
  2. state管理方案:

    • UI相关的组件内部可以维护的状态,在组件内部自己来维护;
    • 大部分需要共享的状态,都交给 redux 来管理和维护;
    • 从服务器请求的数据(包括请求的操作),交给 redux 来维护;
  3. 根据不同的情况会进行适当的调整

总结Redux的使用步骤,包括原始Redux用法和RTK用法。

  • 原始的Redux的使用步骤

    • 先从react-redux中导入Providre包裹根组件

    • 将导出的store绑定到Provider组件的store属性中

    • 创建store,目录结构

      • actionCreator ---> 创建action对象
      • constant ---> 定义常量数据
      • reducer ---> 处理action对象,返回最新的state
      • index ---> 人口文件,创建store,使用中间件
    • 组件中的使用方式

      • 定义函数 ---> mapStateToProps ---> 将store中的数据映射要组件的props中
      • 定义函数 ---> mapDispatchToProps ---> 将dispatch的操作映射到props中
      • 从react-redux中导入高阶组件对要导出的组件进行包裹,并把定义的函数传入connect函数
      • 组件触发相应的事件,dispatch相应的对象,store中的数据改变,组件重新渲染
  • RTK用法

    • 先从react-redux中导入Providre包裹根组件

    • 将导出的store绑定到Provider组件的store属性中

    • 创建store,目录结构:

      • index.js ---> 入口文件 ---> 创建和配置store ---> 主要是合并render

      • features ---> 要管理的数据模块

        • 使用createSliceAPI创建一个slice对象
        • name ---> 配置slice对象的名称
        • initialState ---> 定义初始值
        • reducer ---> 定义reduce函数的对象
        • 导出slice对象的actions---> 组件中使用或者自己内部使用,
        • 导出slice对象的reducer---> index文件合并reducer