Running Next.js application in dev mode, a runtime error occurs, saying "ReferenceError: Cannot access X before initialization"

533 Views Asked by At

I am using next.js version 13.4. I made a Provider using ContextAPI in react(, let's say it's AuthContext) and wrap the whole application with it in RootLayout component. The RootLayout is placed in app/layout.tsx.

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        <GlobalErrorBoundary>
          <AuthProvider>{children}</AuthProvider>
        </GlobalErrorBoundary>
      </body>
    </html>
  );
}

There's no error in build time but when I run npm run dev, the browser yelled that "Unhandled Runtime Error" occurs. And saying, "ReferenceError: Cannot access 'AuthContext' before initialization".

When I build the application and npm run start, it works well. It only happens when I run npm run dev. What's worse is this runtime error happens randomly, quite often, though.

When I look into the react's devTool, theres' an error in ReactDevOverlay, which has a children named <DevRootNotFoundBoundary />. The error message is saying, "The above error occurred in the component". enter image description here

Edit:

Here is a simple version of the Auth.tsx (for declaring ContextAPI) code:

"use client";

export const AuthContext =
    createContext<AuthContextType | null>(null);

export const AuthProvider = ({
    children,
}: PropsWithChildren) => {
    const [token, setToken] =
        useStateWithLocalStorage<AuthTokenType>("token");
    const newToken = useTokenRefresh(token);

    useEffect(() => {
        if (newToken === undefined) {
            return;
        }

        setToken(newToken);
    }, [newToken]);

    useMemo(() => {
        if (token === null) {
            return;
        }
        addTokenToHttpClient(token);
    }, [token]);

    return (
        <AuthContext.Provider
            value={{
                token,
                setToken,
            }}
        >
            {children}
        </AuthContext.Provider>
    );
};

export const useAuthContext = () => {
    const context: AuthContextType | null = useContext(
        AuthContext
    );

    if (context === null) {
        throw new Error();
    }

    return context;
};

You might want to know the inside of useStateWithLocalStorage used above. The possible errors occuring when referring to localStorage in the server is covered by below logic.

if (typeof window !== "undefined") {
    return localStorage.getItem(key);
}

Edit:

I found a suspicious point in codebase, but cannot figure out the exact reason of it. Can anyone help me with this?

My Auth.tsx file is under app/contexts/withLocalStorage/ directory. Inside this directory there are 2 different Context files other than Auth.tsx file and plus, index.ts file. Every variables of Context files are exported through index.ts.

Below is index.ts.

"use client";

export * from "./Example1";
export * from "./Example2";
export * from "./Auth";

The only difference in Auth and other 2 example files is, in Auth.tsx, it's using useEffect and useMemo. When I comment out the line above export * from "./Auth"; in index.ts, the ERROR DISAPPEARS!.

It seems index.ts is trying to import modules in server-side. But a Context file using React's client-side API is not created yet at that time. "use client"; directive in index.ts doesn't work in this case. But why? Or if so, is there any workaround to use useEffect or useMemo in context files?

0

There are 0 best solutions below