我们在使用 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
}
我们只需将利用combineReducers
将reducers
组合到一起,然后创建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
的实现原理是通过传入的name
,reducerName
来规范化action
,并在数据更新时,使用了immer
来保证数据的不可变性。