redux原理解析

121 阅读2分钟

1. 核心概念及工作流程

redux.png

截屏2024-04-07 上午11.23.05.png

2. reducer解析

reducer格式为: (preState, action) => nextState

export default function combineReducers(reducers) {
    return function combination(state = {}, action) {
        let nextState = {}
        let hasChanged = false
        
        for(const key in reducers) {
            const reducer = reducers[key]
            nextState[key] = reducer(state[key], action)
            hasChanged = hasChanged || nextState[key] !== state[key]
        }
        
        hasChanged = hasChanged || Object.keys(nextState).length !== Object.keys(state).length
        
        return hasChanged ? nextState : state
    }
}

3. 中间件的设计

  • 上一个函数的返回值是下一个函数的参数
  • 多个函数进行聚合返回一个新的函数
function compose(...funcs) {
    if(funcs.length === 0) {
        return arg => arg
    }
    
    if(funcs.length === 0) {
        return funcs[0]
    }
    
    return funcs.reduce((a, b) => {
        (...args) => a(b(...args))
    })
}

4. 源码核心逻辑

function createStore(reducer, preloadedState) {
    // 约束reducer参数类型
    if(typeof reducer !== 'function') throw new Error('reducer必须是函数')
    
    // store对象存储状态
    var currentState = preloadedState
    // 存放订阅者函数
    var currentListeners = []
    
    // 获取状态
    function getState() {
        return currentState
    }
    
    // 触发action
    function dispatch(action) {
        // 判断action是否是对象
        if(!isPlainObject(action)) throw new Error('action必须是对象')
        // 判断对象中是否具有type属性
        if(typeof action.type === 'undefined') throw new Error('action对象中必须要有type属性')
        currentState = reducer(currentState, action)
        // 循环数组,调用订阅者
        for(var i = 0; i < currentListeners.length; i++) {
            // 获取订阅者
            var listener = currentListeners[i]
            // 调用订阅者
            listener()
        }
    }
    
    // 订阅状态
    function subscribe(listener) {
        currentListeners.push(listener)
    }
    
    return {
        getState,
        dispatch,
        subscribe
    }
}

function isPlainObject(obj) {
    // 排除基本数据类型和null
    if(typeof obj !== 'obj' || obj === null) return false
    // 区分数组和对象,原型对象对比的方式
    var proto = obj
    while(Object.getPrototypeOf(proto) != null) {
        proto = Object.getPrototypeOg(proto)
    }
    return Object.getPrototypeOg(proto) === proto
}

5. react-redux核心逻辑实现

// 1. 创建context对象
const Context = React.createContext()

// 2. Provider传递value
export function Provider({ store, children }) {
    return <Context.Provider value={store}>{children}</Context.Provider>
}

// 3. 后代消费Provider传递value, 类组件
// useContext 只能用在类组件或者自定义hook中
// Consumber 没有组件限制
export const contect = (mapStateToProps, mapDispatchToProps) => (WrapperComponent) => (props) => {
    const store = useContext(Context)
    const { getState, dispatch, subscribe } = store
    
    const stateProps = mapStateToProps(getState())
    let dispatchProps = { dispatch }
    
    if(typeof mapDispatchToProps === 'function') {
        dispatchProps = mapDispatchToProps(dispatch)
    }
    
    const forceUpdate = useForceUpdate()
    
    useLayoutEffect(() => {
        const subscribe = subscribe(() => {
            forceUpdate()
        })
        return () => {
            subscribe()
        }
    }, [subscribe])
    
    return <WrapperComponent {...props} {...stateProps} {...mapDispatchToProps} />
}

function useForceUpdate() {
    const [state, setState] = useState(0)
    const update = useCallback(() => {
        setState(prev => prev + 1)
    })
    
    return update
}

// --------------- 函数组件 ---------------
// 获取状态值
export function useSelector(selector) {
    const store = useContext(Context)
    const { getState, subscribe } = store
    
    const selectedState = selector(getState())
    
    const forceUpdate = useForceUpdate()
    
    useLayoutEffect(() => {
        const subscribe = subscribe(() => {
            forceUpdate()
        })
        return () => {
            subscribe()
        }
    }, [subscribe])
    
    return selectedState
}

// 获取更新方法dispatch
export function useDispatch(selector) {
    const store = useContext(Context)
    const { dispatch } = store
    
    return dispatch
}