I have built a global state redux like pattern with context and hooks. Is there a way to combine reducers?
Asked Answered
P

1

5

I've built a global state provider context, and a reducer with the useReducer Hook. I'm realizing that combining multiple reducers like I would in redux is problematic. Is there a good way to do this? I have seen people importing combineReducers from redux itself, and that seems like it kind of defeats the point. Does anyone have any insight on this?

Pokorny answered 8/3, 2019 at 19:47 Comment(2)
Sounds like you should consider start using redux instead of the hooks and enjoy some nice tooling ;) Although the redux hook has less overhead for simple usages, I don't see the benefit for multiple combined reducers. That being said, a plain combineReducers is fairly simple to implement.Bortz
you're right. However, I started this particular project to learn hooks INSTEAD of redux. Redux makes this a lot simpler, but I dont want to rely on it.Pokorny
U
6

Not sure this is what you're looking for, but I have used something like below to combine multiple reducers. It actually reduces the reducers. Not actually like redux combineReducers with key/value.

const reduceReducers = (...reducers) => (prevState, value, ...args) =>
  reducers.reduce(
    (newState, reducer) => reducer(newState, value, ...args),
    prevState
  );

I would be used like:

function reducerA(state, action) {
  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + 1 };
    case "decrement":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}

function reducerB(state, action) {
  switch (action.type) {
    case "double":
      return { ...state, count: state.count * 2 };
    case "halve":
      return { ...state, count: state.count / 2 };
    default:
      return state;
  }
}

export default reduceReducers(reducerA, reducerB);

Then the Component:

import reducers from "./reducers";

function Counter({ initialState = { count: 1 } }) {
  const [state, dispatch] = useReducer(reducers, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "double" })}>x2</button>
      <button onClick={() => dispatch({ type: "halve" })}>/2</button>
    </>
  );
}
Uptake answered 8/3, 2019 at 21:42 Comment(3)
gonna give this a shot. this looks like exactly what i was looking for. I'll let you know how it works out! i attempted something similar but was unsuccessful. I am a junior, and I am very familiar with map/filter but still sometimes struggle with the reduce api.Pokorny
this is the correct answer. I was able to do it after spending some time with curried functions. I am discovering through doing all of this though that Redux really is a lot simpler to get the same result. This method has issues like handling initialState. short of using object spread and merging seperate initialStates, or maintaining a giant object in the file I have the context declaration/provider in, there isn't a good way.Pokorny
@DaveFunk For handling initialState We can combine multiple states as below const initialState = { reducerAState, reducerBState } and then pass to useReducerAspic

© 2022 - 2025 — McMap. All rights reserved.