jtwang7 / React-Note

React 学习笔记
8 stars 2 forks source link

Hooks - 模拟实现 redux (useReducer 合并多个 reducer) #27

Open jtwang7 opened 3 years ago

jtwang7 commented 3 years ago

Hooks - 模拟实现 redux (useReducer 合并多个 reducer)

参考文章:

前言

在 redux 中,分离 reducer 可以更好的帮助开发者归纳业务逻辑,同时 redux 也提供了combineReducers 用于收集散落的 reducer,合并成完整的 reducer 进行调用。而在 hooks 中,并没有现成的合并 reducer 的方法,现用 hooks 模拟实现简化版的 redux 供大家学习和参考。

hooks-redux

import React, { createContext, useReducer } from 'react';
// 导出 Context 对象,供消费组件调用
export const hooksContext = createContext('context');
// Context.Provider
const Provider = hooksContext.Provider
// 合并多个 reducer
function combineReducers(reducers) {
    // 返回一个整合后的 reducer 函数
    return function (state = {}, action) {
        // reducer 函数返回最新的 state 对象
        return Object.keys(reducers).reduce((newState, key) => {
            newState[key] = reducers[key](state[key], action);
            return newState;
        }, {});
    }
}
// 外层函数
export const withContext = (reducer, initialState) => {
    let stateKeysLength = Object.keys(initialState).length;
    let reducerKeysLength = typeof reducer === 'function' ? 1 : Object.keys(reducer).length;
    if (stateKeysLength !== reducerKeysLength) {
        throw Error('The length of reducer is not equal the length of initialState')
    }
    // 合并 reducer 
    let combinedReducer = combineReducers(reducer);
    // 高阶函数:接受一个包裹组件
    return InnerComp => {
        // 返回包装后的新组件(可以是函数组件)
        return () => {
            // 在函数组件中,我们就可以使用 hook 了,因此此处不返回类组件
            const [state, dispatch] = useReducer(combinedReducer, initialState);
            return (
                <Provider value={{ state, dispatch }}>
                    <InnerComp />
                </Provider>
            )
        }
    }
} 

重难点

reducer 是怎样的形式?

const reducer = {
  one: function () {...},
  two: function () {...},
  ...
}

如上,将多个 reducer 以对象形式存储

combineReducers 核心代码解释

Object.keys(reducers).reduce((newState, key) => {
    newState[key] = reducers[key](state[key], action);
    return newState;
}, {});

已知 reducers 如上所示,为一个包含多个 reducer 的对象,Object.keys() 获取的是各键名,通过 array.reduce() 遍历,依次调用 reducers 对象中的 reducer 函数 (调用方式和普通 reducer 调用一样,传入上一次的 state 和本次 action),并将结果保存到新的状态中。

这里要保证每个 reducer 在没有匹配到 action 时,返回原 state,也就说,每个 reducer 中 switch 方法的 default 必须返回 state。

switch (xxx) {
...
default:
return {...state} // 必须返回新的 immutable state
}

且该处需要遍历所有 reducer,应该是可以优化的一个点

高阶组件

高阶组件本质是一个函数包裹的过程。它接收一个组件,并返回一个新的组件。而在这过程中,我们可以为新的组件添加一些额外的操作。 高阶组件可以返回类组件,也可以返回函数组件,但要注意的是,像 useReducer 这些 hooks,必须在函数组件的顶层使用。