#Reducer Context undefined in root.tsx

1 messages · Page 1 of 1 (latest)

thorny mauve
#

Posted originally in General.

Does anyone know why a context provider may not have a value ready despite definitely being within a provider? Maybe a question for the help forum but the context looks sort of like:

import { useReducer, createContext, useContext } from "react";
import { ReduceFunc } from "reducer";
import type { ReducerState, ReducerActions } from "reducer";
import type { ReactNode, Dispatch } from "react";

type ReducerContextType = [ReducerState, Dispatch<ReducerActions>];

const ReducerContext = createContext<ReducerContextType | undefined>(undefined);

function ReducerProvider({ children }: { children: ReactNode }) {
  const [state, dispatch] = useReducer(ReduceFunc, {/*default state*/});

  return <ReducerContext.Provider value={[state, dispatch]}>{children}</ReducerContext.Provider>;
}

function useRootReducer() {
  const context = useContext(ReducerContext);
  if (context === undefined) {
    throw new Error("useSidebar must be used within a SidebarProvider");
  }
  return context;
}

export { ReducerProvider, useRootReducer };
 
#

Usage looked like:

  const DocumentWithProviders = ({children} : {children: ReactNode[]}) => {
    return (
      <ReducerProvider>
        <Document>
          {children}
        </Document>
      </ReducerProvider>
    )
  }
  const App = () => {
    // This fails as the context is still undefined by the time this runs.
     const [state, dispatch] = useRootReducer();
    return (<DocumentWithProviders>
       {/* app code */}
  </DocumentWithProviders>)
  }
#

This was done in root.tsx of the app and Document is:

const Document = ({ children }: { children: ReactNode; }) => {
  return (
    <html lang="en" >
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
};
#

I was unable to get that solution to work despite several different attempted changes. Eventually I was able to get something that's usable by removing the definition of ReducerProvider altogether.

A (somewhat annoying but I can deal with it) solution looks like, :

  const App = () => {
    const [state, dispatch] = useReducer(ReduceFunc, {/*default state*/});
    
  return (
  <DocumentWithProviders>
    <ReducerContext.Provider value={[state, dispatch]}>
    {/*Rest of app code then had access to state and dispatch via useRootReducer with no issues*/}
    </ReducerContext.Provider>;
  </DocumentWithProviders>
  }
#

I wonder if this somehow has to do with how the document is prepared on the server? I've done a lot of contexts and never ran into this issue before, so just a really strange thing I ran into.

south loom
#

Looks to me like your problem is because useRootReducer is called outside of DocumentWithProviders

south loom
south loom
#

The react documentation explains it like so: https://react.dev/reference/react/useContext

useContext() always looks for the closest provider above the component that calls it. It searches upwards and does not consider providers in the component from which you’re calling useContext().

thorny mauve
#

Eh actually, I think I get it. I’m calling useRooy at the same time I’m starting to create the provider

#

I thought I had used those other providers at the same level but when I think about it, it was always deeper in the app

#

I’m pretty familiar with context, so I get that it’s undefined until it’s initialized. It just wasn’t clicking that my flow for the app was App -> useRoot -> Create root provider. Thought “I can’t be wrong, the computer must be wrong” lol

#

Thanks for the help!