【源码共读】| 简易实现redux-toolkit

我们在使用 Redux 时可能会遇到了以下几个问题:

● 配置 Store 复杂。

● 需要添加许多包来实现异步、日志等功能。

● 基于 Redux 的灵活性,容易导致千差万别的编写方式。

为了解决这些问题,官方推出了一套类似脚手架的工具,以规范化 Redux 的使用。那么,如果让我们来实现一个rtk,应该怎么做呢?

简单使用

以官方文档为例,让我们先看一下用法。
redux-toolkit.js.org/tutorials/q...

官网上有详细的教程,这里不再赘述,总结如下几点:

  • 创建rtkStore
  • 使用React-Redux的Provider传递store
  • 创建React State Slice
  • 在组件中调用

简单实现

那么这个过程是如何实现的呢?以计数器为例,首先要实现的是configureStore

configureStore

对应源码:github.com/reduxjs/red... target:

  • 传入一个或多个reducer
  • 构建出redux store
tsx 复制代码
// 计数器的例子
export default configureStore({
  reducer: {
    counter: countReducer
  }
})
tsx 复制代码
import { combineReducers, createStore } from 'redux'
export function configureStore({ reducer }) {
  const rootReducer = combineReducers(reducer)

  const store = createStore(rootReducer)

  return store
}

我们只需将利用combineReducersreducers组合到一起,然后创建store

当然,在源码中传入中间件并进行增强。由于这只是一个简易实现,所以只完成了核心功能的部分。

createSlice

对应源码:github.com/reduxjs/red... target:

  • 传入name, initialState, reducers
  • 返回一个处理好的对象
javascript 复制代码
{
    name,
    actions: actionCreators,
    reducer: (state, action) => {
      if (!_reducer) _reducer = buildReducer()

      return _reducer(state, action)
    }
  }

处理actions
tsx 复制代码
import createAction from './createAction'
import createReducer from './createReducer'

export default function createSlice({ name, initialState, reducers }) {
  const reducerNames = Object.keys(reducers)

  // 保存action创建函数
  const actionCreators = {}

  reducerNames.forEach((reducerName) => {
    const type = `${name}/${reducerName}`
    // 创建对应的action
    // sample: { type: 'counter/increment', payload: 1}
    actionCreators[reducerName] = createAction(type)
  })


  return {
    // 返回一个对象,包含name、actions和reducer属性
    name,
    actions: actionCreators,
    
  }
}
tsx 复制代码
// createAction
export default function createAction(type) {
  function actionCreator(...args) {
    return { type, payload: args[0] }
  }

  actionCreator.type = type
  return actionCreator
}

处理reducer

上面我们处理了action,接下来处理reducer部分

tsx 复制代码
import createAction from './createAction'
import createReducer from './createReducer'

export default function createSlice({ name, initialState, reducers }) {
  // 省略...
  // 保存reducer
  const sliceCaseReducersByType = {}

  reducerNames.forEach((reducerName) => {
    // 取出reducers中的每个reducer函数
    const maybeReducerWithPrepare = reducers[reducerName]
    const type = `${name}/${reducerName}`
    sliceCaseReducersByType[type] = maybeReducerWithPrepare
    actionCreators[reducerName] = createAction(type)
  })

  function buildReducer() {
    return createReducer(initialState, (builder) => {
      for (let key in sliceCaseReducersByType) {
        // @key:counter/incremen
        // @sliceCaseReducersByType[key]:state => {
        //   state.value += 1;
        // }
        builder.addCase(key, sliceCaseReducersByType[key])
      }
    })
  }

  let _reducer

  return {
    // 返回一个对象,包含name、actions和reducer属性
    name,
    actions: actionCreators,
    reducer: (state, action) => {
      if (!_reducer) _reducer = buildReducer()

      // 调用_reducer函数处理state和action,并返回处理后的state
      return _reducer(state, action)
    }
  }
}

executeReducerBuilderCallback的作用

  • 返回标准的builder对象
tsx 复制代码
// createReducer
import createNextState from 'immer'

export default function createReducer(initialState, mapOrBuilderCallback) {
  // 获取存储action和reducer的对象
  let [actionMap] = executeReducerBuilderCallback(mapOrBuilderCallback)

  function reducer(state = initialState, action) {
    // 获取action对应的reducer数组
    const caseReducers = [actionMap[action.type]]
    // 依次执行reducer
    // 利用immer库,返回一个新的state
    return caseReducers.reduce((previousState, caseReducer) => {
      if (caseReducer) {
        return createNextState(previousState, (draft) => {
          return caseReducer(draft, action)
        })
      }
      return previousState
    }, state)
  }

  return reducer
}

function executeReducerBuilderCallback(mapOrBuilderCallback) {
  const actionMap = {}

  const builder = {
    addCase: (type, reducer) => {
      actionMap[type] = reducer
      return builder
    }
  }
  // 传入一个标准的builder对象,该对象包含addCase方法
  mapOrBuilderCallback(builder)

  // 使用数组的目的是为了方便解构自定义变量名字
  return [actionMap]
}
tsx 复制代码
// createSlice
import createAction from './createAction'
import createReducer from './createReducer'

export default function createSlice({ name, initialState, reducers }) {
  const reducerNames = Object.keys(reducers)

  // 保存action创建函数
  const actionCreators = {}
  // 保存reducer
  const sliceCaseReducersByType = {}

  reducerNames.forEach((reducerName) => {
    const maybeReducerWithPrepare = reducers[reducerName]
    const type = `${name}/${reducerName}`
    // 将传入的reducer函数赋值给sliceCaseReducersByType对象中的type属性
    sliceCaseReducersByType[type] = maybeReducerWithPrepare
    // sample: { type: 'counter/increment', payload: 1}
    actionCreators[reducerName] = createAction(type)
  })

  function buildReducer() {
    return createReducer(initialState, (builder) => {
      for (let key in sliceCaseReducersByType) {
        builder.addCase(key, sliceCaseReducersByType[key])
      }
    })
  }

  let _reducer

  return {
    name,
    actions: actionCreators,
    reducer: (state, action) => {
      if (!_reducer) _reducer = buildReducer()

      // 返回处理后的state
      return _reducer(state, action)
    }
  }
}

至此,我们已经完成了rtk中的核心功能

  • 规范化action
  • 处理更新后的state

总结

通过实现简易的rtk,我们可以了解到rtk的实现原理是通过传入的namereducerName来规范化action,并在数据更新时,使用了immer来保证数据的不可变性。

相关推荐
耶啵奶膘14 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具4 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test5 小时前
js下载excel示例demo
前端·javascript·excel
Yaml45 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事5 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro