一文掌握Redux-toolkit核心原理

是什么

redux-toolkit 是redux官方封装和标准化的工具集,其主要作用是简化redux状态库的创建和使用。它内置了Immer、redux-thunk和reselect。redux-toolkit为我们做了下面这些事情:

  1. 简化store创建
  2. 简化reducer创建
  3. 简化不可变更新的实现
  4. 内置常用中间件,简化设置常用中间件的逻辑代码
  5. 自动生成action生成函数,简化action创建
  6. dispatch异步逻辑标准化

怎么用

redux-toolkit的基本使用:

起始于两个关键API来简化Redux应用中一些公共的操作

configureStore:简化store的创建,自动组合reducer、添加常用中间件。

createSlice:简化reducer的创建,集成Immer库,避免使用扩展运算实现不可变更新。通过slice name和reducer name创建action生成器。

jsx 复制代码
//创建reducer
const initialState = {
  userName: "张三",
  signature: "早"
}
//创建userSlice
const userReducer = createSlice({
  name: 'user', // 切片名称
  initialState, // 初始状态
  reducers: { // 定义reducer函数,用于处理action
    setUserName: (state, action) => {
      state.userName = action.payload
    },
    setSignature: (state, action) => {
      state.signature = action.payload
    }
  }
})

//创建store
const store = configureStore({
  reducer: {
    user: userReducer.reducer,
    product: productReducer.reducer,
  },
});

从基本使用可以看出:

  • redux-toolkit简化了reducer的创建,不需要大量的switch case语句控制reducer流程,它内部进行处理。
  • redux-toolkit会根据reducer配置创建同名action生成器并挂载到slice对象的actions属性下。通过调用action生成器,其内部会绑定对应的type,传入生成器的数据挂载到action.payload上。
  • 在reducer中,我们可以看到这样的代码 state.userName = action.payload ,这意味着不需要通过扩展运算符来保证不可变更新。
  • 所以redux-toolkit简化了reducer和action创建的样板代码。
  • slice还提供了其他的Api,比如selectSlice、getSelectors这些状态访问器,这些我们在结合react-redux使用时再介绍。

高级用法

  • store增强。内置Redux DevTools集成工具
  • 中间件。内置redux-thunk、Serializable State Invariant(开发环境)、Immutable State Invariant(开发环境)

这里主要介绍redux-toolkit中如何配置增强器和中间件以及redux-thunk中间件的作用、实现原理及其扩展。

  • 如何在redux-toolkit中配置中间件和增强器

    jsx 复制代码
    const store = configureStore({
      reducer: {
        user: userReducer.reducer,
        product: productReducer.reducer,
      },
      middleware: (getDefaultMiddleware) => {
        //获取默认中间件
        const middleware = getDefaultMiddleware();
        return middleware.concat(yourMiddleware);
      },
      enhancers: (getDefaultEnhancers) => {
        //获取默认增强器
        const enhancers = getDefaultEnhancers();
        //返回
        return enhancers.concat(yourEnhancers);
      },
    });

    middleware和enhancers配置项接收一个回调函数,并将getDefaultMiddleware和getDefaultEnhancers传递给回调函数,这两个方法默认获取内置的所有增强器和中间件,也可根据传入的配置获取特定的中间件。configureStore根据middleware和enhancers配置返回的结果创建增强器和中间件。

  • redux-thunk的作用和原理

    redux-thunk是一个中间件,它提供dispatch 函数类型action的代码执行机制(默认情况下,action是一个对象)。当action是一个函数时,则执行这个函数。其实现原理如下:

    jsx 复制代码
    function thunkMiddleware({ dispatch, getState }) {
      return (next) => (action) => {
        if (typeof action === "function") {
          return action(dispatch, getState);
        }
        return next(action);
      };
    }

    为什么需要创建这种机制?在Redux中,如果要在dispatch到reducer的过程中执行一些额外的逻辑时,需要通过中间件来插入,但是中间件的弊端是,在每次dispatch时,所有的中间件都会执行。这种机制在某些场景中就显得不太适用,所以通过dispatch函数类型的action,让我们在需要的时候才执行额外的代码逻辑,我们称这些特定的代码为thunk。

  • createAsyncThunk

    createAsyncThunk是为redux-thunk针对异步thunk的创建而设计,规范异步thunk的执行流程,简化异步thunk的代码组织。createAsyncThunk的使用和基本原理如下:

    jsx 复制代码
    //基本实现
    function createAsyncThunk(typePrefix, payloadCreator) {
      //返回actionCreator函数
      return (thunkArgs) => {
        //获取dispatch函数
        return (dispatch, getState) => {
          (() => {
            //调用payloadCreator函数,获取异步操作的结果
            const result = await payloadCreator(thunkArgs, { dispatch, getState })
            //根据异步操作的结果,dispatch不同的action
            if (result) {
              dispatch({ type: `${typePrefix}/fulfilled`, payload: result })
            } else {
              dispatch({ type: `${typePrefix}/rejected` })
            }
          })()
        }
      }
    }
    
    //创建asyncThunk
    const fetchUserInfo = createAsyncThunk(
      'user/fetchUserInfo',
      async (userId, thunkAPI) => {
        console.log(userId, thunkAPI)
        const response = await new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve("李四")
          }, 1000);
        });
        // const data = await response.json();
        return response;
      }
    );
    
    
    //createAsyncThunk会自动创建padding、fulfilled、rejected状态的action,并在对应状态时dispatch,所以需要在slice上对原reducer进行扩展
    const userReducer = createSlice({
      name: 'user', // 切片名称
      initialState, // 初始状态
      reducers: { // 定义reducer函数,用于处理action
        setUserName: (state, action) => {
          state.userName = action.payload
        },
        setSignature: (state, action) => {
          state.signature = action.payload
        }
      },
      extraReducers(builder) {
        builder.addCase(fetchUserInfo.fulfilled, (state, action) => {
          console.log(action)
          state.userName = action.payload
        })
        builder.addCase(fetchUserInfo.rejected, (state, action) => {
          state.userName = "张三"
        })
        builder.addCase(fetchUserInfo.pending, (state, action) => {
          state.userName = "加载中..."
        })
      },
    })
    
    //在UI中使用
    function UserInfo() {
      function handleClick() {
        store.dispatch(fetchUserInfo(1));
      }
      return(
        <>
          <h1>用户信息</h1>
          <div>姓名: 张三</div>
          <button onClick={handleClick}>修改姓名</button>
        </>
      )
    }
  • 定制action的payload。在某些情况下,我们需要对payload做一些统一处理时,需要将这些统一处理逻辑集中处理(数据校验、添加公共属性等)。createSlice在配置中实现了这一机制。通过改造reducers配置项,把需要处理的reducer改为一个对象,对象包含reducer和prepare。dispatch时,先执行prepare对payload统一处理后再创建action。需要注意的是,prepare回调函数必须返回带有payload属性的对象。使用如下:

    javascript 复制代码
    const userReducer = createSlice({
      name: 'user', // 切片名称
      initialState, // 初始状态
      reducers: { // 定义reducer函数,用于处理action
        //将reducer配置成对象
        setUserName:{
          reducer:  (state, action) => {
            state.userName = action.payload
          },
          //payload预处理
          prepare: (userName) => {
            //必须返回带有payload属性的对象
            return { payload: userName }
          }
        },
        setSignature: (state, action) => {
          state.signature = action.payload
        }
      },
    })

总结:redux-toolkit对redux状态管理库从创建到使用进行了一些列的封装和扩展,内置常用功能扩展库和样板代码的处理逻辑。让开发者通过更少的代码实现更强大的状态管理模块。redux-toolkit的整体还是围绕reducer、action、select、store增强和中间件几大核心进行封装,提供dispatch异步逻辑处理规范等。本文中还有许多细节问题没有展开讨论,但已经包含了redux-toolkit的核心内容和使用场景。

相关推荐
云枫晖3 小时前
手写Promise-静态方法all和allSettled
前端·javascript
weixin_456904273 小时前
前端开发时npm install报错解决方案
前端·npm·node.js
东华帝君3 小时前
Object.create继承
前端
王嘉俊9253 小时前
Flask 入门:轻量级 Python Web 框架的快速上手
开发语言·前端·后端·python·flask·入门
风继续吹..3 小时前
Express.js 入门指南:从零开始构建 Web 应用
前端·javascript·express
一只小风华~3 小时前
命名视图学习笔记
前端·javascript·vue.js·笔记·学习
编程点滴3 小时前
前端项目从 Windows 到 Linux:构建失败的陷阱
linux·前端
小高0073 小时前
🎯GC 不是 “自动的” 吗?为什么还会内存泄漏?深度拆解 V8 回收机制
前端·javascript·面试
RoyLin4 小时前
V8引擎与VM模块
前端·后端·node.js