资源:
- redux/toolkit 英文文档
- redux/toolikt中文文档
- redux-toolkit源码
- redux最佳实践
- immer文档
目标
- 掌握rtk用法
- 实现rtk内的configureStore, createSlice, createReducer
用法
import { configurStore } from '@reduxjs/toolkit'
import counterReducer from './counterReducer'
const store = configureStore({
reducer: {
counter: counterReducer
}
})
export default store
- counterReducer写法, 有点像dva的models
import { createSlice } from '@reduxjs/toolkit'
const counterReducer = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1
}
}
})
export const { increment } = counterReducer.actions
export default counterReducer.reducer
import React, { useLayoutEffect, useReducer } from 'react'
import store from './../store'
import { increment } from '../store/counterReducer'
export default function RtkPage() {
const count = store.getState().counter.value
const [, forceUpdate] = useReducer(x => x+1, 0)
useLayoutEffect(() => {
const unsubscribe = store.subscribe(() => {
forceUpdate()
})
return () => {
unsubscribe()
}
}, [])
return (
<div>
<button onClick={() => store.dispatch({type: 'counter/increment'})}>{count}</button>
<button onClick={() => store.dispatch(increment())}>{count}</button>
</div>
)
}
- 首先实现configureStore
- 思考下configureStore 干了哪些事情,返回了什么
-
- 创建store 2.返回store
import { createStore } from 'redux'
function configureStore({reducer}){
const rootReducers = combineReducers(reducer)
const store = createStore(rootReducers)
return store
}
export {configureStore}
- 入参是什么, 返回了什么 入参为 name, initialState, reducers, 返回name, acitons, reducer
- 我们需要的actions 是什么样的 {type: 'counter/increment', payload: payload} / (incement())
- type需要我们自己去拼接, 那么拼接需要用到的都是什么, 第一个reducer的name, 第二个 reducer的键名 所以第一步我们需要获取reducer的键名数组, 第二步需要通过这个type去返回需要的type,payload
- reducer需要返回redux需要的reducer函数,所以需要createReducer帮助我们去创建一个reducer
function actionCreator(type) {
function creatorActions(...args) {
return {
type,
payload: args[0]
}
}
return creatorActions
}
function createSplice({ name, initialState, reducers }){
const reducerNames = Object.keys(reducers)
let actionsMap = {}
let sliceCaseReducersByType = {}
reducerNames.forEach(reducerName => {
const r = reducers(reducerName)
const type = `${name}/${reducerName}`
sliceCaseReducersByType[type] = r
actionsMap[reducerName] = actionCreator(type)
})
function buildReducer(){
return createReducer(initialState,(builder) => {
for(let key in sliceCaseReducersByType) {
builder.addCase(key, sliceCaseReducersByType[key])
}
})
}
let _reducer
return {
name,
actions: actionsMap,
reducer: (state, action) => {
if(!_reducer) _reducer = buildReducer()
return _reducer(state,action)
}
}
}
- 下面我们实现createReducer这个api
- 思考下:
- 使用这个api的时候我们传递了默认值和一个callback并且返回一个新的reducer
- 那我在这个api中要通过这个callback函数进行创建reducer
- 先去实现callback这个回调函数, 这个回调函数会创建一个addCase的方法,并且返回拼接后的type为键名,用户输入的reducer为键值的一个对象
function createReducer(initialState, mapOrBuilderCallback){
let [actionsMap] = executeReducerBuilderCallback(mapOrBuilderCallback)
function reducer(state = initialState, action) {
let caseReducers = [actionsMap[action.type]]
return caseReducers.reduce((previousState, caseReucer) => {
if(caseReducer) {
return createNextState(previousState, (draft) => {
return caseReucer(draft, action)
})
}
return previousState
}, state)
}
return reducer
}
function executeReducerBuilderCallback(mapOrBuilderCallback) {
let actionsMap = {}
const builder = {
addCase: (type, reducer) => {
actionsMap[type] = reducer
return builder
}
}
mapOrBuilderCallback(builder)
return [actionsMap]
}