reducer工程化 | 青训营笔记

71 阅读3分钟

reducer工程化

1.reducer的拆分和合并
// vote版块下的reducer
const initial = {
    supNum: 10,
    oppNum: 5,
    num: 0
}
​
export default function voteReducer(state = initial, action) { 
    // 深克隆
    state = { ...state }
    switch(action.type) {
        case 'VOTE_SUP':
            state.supNum++
            break
        case 'VOTE_OPP':
            state.oppNum++
            break
        default:
            break
    }
    return state
}
// Personal版块下的reducer
const initial = {
    num: 100,
    info: null
}
​
export default function PersonalReducer(state = initial, action) { 
    // 深克隆
    state = { ...state }
    switch(action.type) {
        case 'PERSONAL_INFO':
            state.info = action.info
            break
        default:
            break
    }
    return state
}
// 合并各个模块的reducer
import { combineReducers } from 'redux'
import voteReducer from './VoteReducer'
import PersonalReducer from './PersonalReducer'const reducer = combineReducers({
    vote: voteReducer,
    personal: PersonalReducer
})
​
export default reducer
/*
state = {
    vote: {
        supNum: 10,
        oppNum: 5,
        num: 0
    },
    personal: {
        nun: 100,
        info: null
    }
}
*/
1.1combineReducers源码分析
export default function myCombineReducers(reducers) {
    // reducers是一个对象,以键值对存储了:模块名:每个模块的reducer
    let reducersKeys = Reflect.ownKeys(reducers)
    /*
        返回一个合并的reducer
            + 每次dispatch派发,都要把这个reducer执行
            + state就是redux容器中的公共状态
            + action就是派发时候传递进来的行为对象
    */
    return function reducer(state = {}, action) {
        // 把reducers中的每个模块中的reducer执行
        let nextState = {}
        reducersKeys.forEach(key => {
            let reducer = reducers[key]
            nextState[key] = reducer(state[key], action)
        })
    }
}
2.派发行为标识宏管理

问题:每一次dispatch派发的时候,都会去每个模块的reducer中找一遍,把所有和派发行为标识匹配的逻辑执行,很可能由于派发的行为的标识名称相同导致冲突

解决办法:基于宏管理(统一管理),让所有派发的行为标识,具有唯一性

统一管理需要派发的行为标识

  • 为了保证不冲突,我们一般都是这么命名的:模块名__派发的行为标识(大写)
  • 变量和存储的值是一致的
  • 所以需要派发的行为标识,都在这里定义
export const VOTE_SUP = "VOTE_SUP"
export const VOTE_OPP = "VOTE_OPP"
export const PERSONAL_SUP = "PERSONAL_SUP"
export const PERSONAL_INFO = "PERSONAL_INFO"
3.actionCreator的创建

把派发的行为对象按照模块进行统一管理

import * as TYPES from '../action_types'
// vote板块要派发的行为对象管理
const voteAction = {
    support() {
        return {
            type: TYPES.VOTE_SUP
        }
    },
    oppose() {
        return {
            type: TYPES.VOTE_OPP
        }
    }
}
​
export default voteAction
import * as TYPES from '../action_types'
// personal板块要派发的行为对象管理
const personalAction = {
    getInfo() {
        return {
            type: TYPES.PERSONAL_INFO
        }
    },
}
​
export default personalAction
// 把各个板块的action合并为一个action
import voteAction from "./voteAction";
import personalAction from "./personalAction";
​
const action = {
    vote: voteAction,
    personal: personalAction
}
export default action

react-redux的基础运用

1、提供provider方法传递上下文对象
import { Provider } from 'react-redux'
<Provider store={store}>
    <Vote />
</Provider>
2、提供connect函数获取公共状态信息绑定
/*
connect(mapStateToProps, mapDispatchProps)(我们要渲染的组件
    1.mapStateToProps:可以获取到redux中的公共状态,把需要的信息作为属性传递给组件
    connect((state) => {
        // 存储redux容器中,所有模块的公共状态信息
        // 返回对象中的信息,就是要作为属性,传递给组件信息
        return {
            supNum: state.vote.supNum,
            info: state.personal.info
        }
    })(Vote)
    2.mapDispatchProps:把需要派发的任务,当作属性传递给组件
    connect(null, 
        dispatch => {
        // dispatch:store.dispatch 
        // 返回对象中的信息,会作为属性传递给组件
        return {
            
        }
    })(Vote)
*/
// 传递公共状态
export default connect(state => {
    return {
        supNum: state.vote.supNum,
        oppNum: state.vote.oppNum,
        num: state.vote.num
    }
})(Vote)
const Vote = function Vote(props) {
    let { supNum, oppNum } = props
}
// 传递派发的任务
export default connect(null, dispatch => {
    return {
        support() {
            dispatch(action.vote.support())
        },
        oppose() {
            dispatch(action.vote.oppose())
        }
    }
})(VoteFooter)
let { support, oppose } = props
解析react-redux源码
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'
import { bindActionCreators } from 'redux'
const ThemeContext = createContext()
​
export function Provider(props) {
    let { store, children } = props
    return <ThemeContext.Provider value={{ store: store }}>
        {children}
    </ThemeContext.Provider>
}
​
export function connect(mapStateToProps, mapDispatchToProps) {
    if (!mapStateToProps) {
        mapStateToProps = () => {
            // 默认什么都不传递给组件
            return {}
        }
    }
    if (!mapDispatchToProps) {
        mapDispatchToProps = (dispatch) => {
            // 默认传递dispatch方法给组件
            return {
                dispatch
            }
        }
    }
    return function currying(Component) {
        // Component: 最终要渲染的组件
        return function HOC(props) {
            // 需要获取上下文中的store
            let { store } = useContext(ThemeContext),
            { getState, dispatch, subscribe } = store
            // 向事件池中加入让组件更新的方法
            let [, forceUpdate] = useState(0)
            useEffect(() => {
                let unsubscribe = subscribe(() => {
                    forceUpdate(+new Date())
                })
                return () => {
                    // 组件释放的时候执行: 把事件池中的函数移除掉
                    unsubscribe()
                }
            }, [])
            // 把mapStateToProps和mapDispatchToProps执行,把执行的返回值作为属性传递给组件
            let state = getState(),
                nextState = useMemo(() => {
                    mapStateToProps(state)
                }, [state])
            let dispatchProps = {}
            if (typeof mapDispatchToProps === 'function') {
                // 是函数直接执行即可
                dispatchProps = mapDispatchToProps(dispatch)
            } else {
                // 是actionCreator对象,需要经过bindActionCreators处理
                dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
            }
            return <Component {...props} {...nextState} {...dispatchProps} />
        }
    }
}