Search code examples
reactjsreact-hooksreducereact-context

How to use combineReducers when having a custom react context hook?


I made a custom context:

interface IContextProps {
  state: StateType;
  dispatch: Dispatch<Action>;
}

const Context = React.createContext({} as IContextProps);

export const useCustomContext = (): IContextProps => {
  return React.useContext(Context);
};

export default Context;

State type:

export type StateType = {
  items: Array<DataItems>;
  global: StateTypeGlobal;
};

I had one big reducer which I wanted to split and combine with combineReducer

export const combineReducers = (slices) => (state, action) =>
  Object.keys(slices).reduce(
    (acc, prop) => ({
      ...acc,
      [prop]: slices[prop](acc[prop], action),
    }),
    state
  );

When I initialize the provider:

  const rootReducer = combineReducers({
    global: globalReducer,
    items: ItemReducer,
  });
  
  const [state, dispatch] = React.useReducer(rootReducer, initalState);

  const store = React.useMemo(() => [state, dispatch], [state, dispatch]);

  <Context.Provider value={store}>
  </Context.Provider>

here I get an error saying:

Type 'any[]' is missing the following properties from type 'IContextProps': state, dispatchts(2739) index.d.ts(328, 9): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & ProviderProps'

Which made sense since I am sending in an Array and not an object. This made me change my customContext to:

const Context = React.createContext([] as Array<IContextProps>);

export const useCustomContext = (): Array<IContextProps> => {
  return React.useContext(Context);
};

export default Context;

Problem

How do I extract state?

In a file I previosly did:

const { state, dispatch } = useCustomContext();

This worked but now gives me the following error:

Property 'state' does not exist on type 'IContextProps[]'.

Property 'dispatch' does not exist on type 'IContextProps[]'.

EDIT:

Stackblitz link


Solution

  • This isn't really related to React or context; you're simply mixing up your types.

    In the first example, your context's type is an object, but you're passing an array to the Provider. A simpler example with the same problem:

    interface IContextProps {
      state: StateType;
      dispatch: Dispatch<Action>;
    }
    
    const example = (value: IContextProps) => {};
    
    const value: any[] = [state, dispatch];
    
    /*
     * Argument of type 'any[]' is not assignable to parameter of type 'IContextProps'.
     * Type 'any[]' is missing the following properties from type 'IContextProps':
     *   state, dispatch ts(2345)
     */
    example(value);
    

    Instead of the array in the useMemo, you want an object that matches the IContextProps interface, e.g.:

    // value is an object
    const store = React.useMemo(() => ({ state, dispatch }), [state, dispatch]);
    
    <Context.Provider value={store}>
    </Context.Provider>