Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine Reducer #47

Open
jtwang7 opened this issue May 13, 2022 · 0 comments
Open

Combine Reducer #47

jtwang7 opened this issue May 13, 2022 · 0 comments

Comments

@jtwang7
Copy link
Owner

jtwang7 commented May 13, 2022

useReducer - combine reducers

参考链接:React useReducer: How to combine multiple reducers?

原文+注解

Combine slice reducers (combineReducers)

The most common approach is to let each reducer manage its own property ("slice") of the state:

独立每个state和reducer的逻辑,将相应的state和reducer打包为一个slice,然后合并。

type A = any;
type B = any;
type STATE = {
  a: A;
  b: B;
}
type ACTION = 
	| {type: 'changeA', payload: any}
	| {type: 'changeB', payload: any}
type REDUCER = <T extends {}>(state: T, action: ACTION) => STATE
type SLICES = {
  a: REDUCER<A>;
  b: REDUCER<B>;
}
// reduce 写法
const combineReducers = (slices: SLICES) => (state:STATE, action:ACTION):STATE =>
  // use for..in loop, if you prefer it
  Object.keys(slices).reduce( 
    (state, key) => ({
      ...acc,
      [key]: slices[key](state[key], action),
    }),
    state
  );

// for...in 写法
const combineReducers = (slices: SLICES) => (state:STATE, action:ACTION):STATE => {
  let newState = {}
  for (let [key, reducer] of Object.entries(slices)) {
    newState = {
      ...state,
      [key]: reducer(state[key], action)
    }
  }
  return newState
}

Example:

import a from "./Reducer1";
import b from "./Reducer2";

const initialState = { a: {}, b: {} }; // some state for props a, b
const rootReducer = combineReducers({ a, b });

const StoreProvider = ({ children }) => {
  const [state, dispatch] = useReducer(rootReducer, initialState);
  // Important(!): memoize array value. Else all context consumers update on *every* render
  const store = React.useMemo(() => [state, dispatch], [state]);
  return (
    <StoreContext.Provider value={store}> {children} </StoreContext.Provider>
  );
};

Combine reducers in sequence

Apply multiple reducers in sequence on state with arbitrary shape, akin to reduce-reducers:

const reduceReducers = (...reducers) => (state, action) =>
  reducers.reduce((acc, nextReducer) => nextReducer(acc, action), state);

Example:

const rootReducer2 = reduceReducers(a, b);
// rest like in first variant

Combine multiple useReducer Hooks

You could also combine dispatch and/or state from multiple useReducers, like:

合并多个 useReducer 实现 combine reducer

主要思想:遍历所有 dispatch,最终会找到 action 所对应的 dispatch 然后执行

const combineDispatch = (...dispatches) => (action) =>
  dispatches.forEach((dispatch) => dispatch(action));

Example:

const [s1, d1] = useReducer(a, {}); // some init state {} 
const [s2, d2] = useReducer(b, {}); // some init state {} 

// don't forget to memoize again
const combinedDispatch = React.useCallback(combineDispatch(d1, d2), [d1, d2]);
const combinedState = React.useMemo(() => ({ s1, s2, }), [s1, s2]);

// This example uses separate dispatch and state contexts for better render performance
<DispatchContext.Provider value={combinedDispatch}>
  <StateContext.Provider value={combinedState}> {children} </StateContext.Provider>
</DispatchContext.Provider>;

In summary

Above are the most common variants. There are also libraries like use-combined-reducers for these cases. Last, take a look at following sample combining both combineReducers and reduceReducers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant