'window not defined' when trying to access it inside a Context
Answered
MaKTaiL posted this in #help-forum
MaKTaiLOP
I'm building a sidebar and I created a SideBarContext to track isSideBarOpen variable. I also want to set a default value based on window size.
Everything is working just fine but I keep getting this error on the console server:
import { createContext, useState } from "react";
export const SideBarContext = createContext({
sideBarOpen: false,
setSideBarOpen: (sideBarOpen: boolean) => {},
toggleSideBar: () => {},
});
export function SideBarProvider({ children }: { children: React.ReactNode }) {
const [sideBarOpen, setSideBarOpen] = useState(
window.innerWidth < 1280 ? false : true,
);
const toggleSideBar = () => {
setSideBarOpen(!sideBarOpen);
};
return (
<SideBarContext.Provider
value={{ sideBarOpen, setSideBarOpen, toggleSideBar }}
>
{children}
</SideBarContext.Provider>
);
}
Everything is working just fine but I keep getting this error on the console server:
ReferenceError: window is not defined
. I tried adding "use client" to the context file but the error still comes up. The weird thing is that the default value behavior is working as intended. Context appears to run server side for some reason. Does anybody have any clue on how to solve this?Answered by MaKTaiL
@European sprat Ok, I figured out how to fix this. It was a combination of two things:
1. First I moved the useState logic inside a useLayoutEffect (useEffect wasn't enough)
2. Second I used the dynamic library from "next/dynamic" inside the providers.tsx to import the SideBarContext:
Now it is working just as intended!
1. First I moved the useState logic inside a useLayoutEffect (useEffect wasn't enough)
useLayoutEffect(() => {
if (window.innerWidth >= 1280) {
setSideBarOpen(true);
}
}, []);
2. Second I used the dynamic library from "next/dynamic" inside the providers.tsx to import the SideBarContext:
"use client";
import { SessionProvider } from "next-auth/react";
import dynamic from "next/dynamic";
export default function Providers({ children }: { children: React.ReactNode }) {
const SideBarProvider = dynamic(() =>
import("@/contexts/SideBarContext").then((mod) => mod.SideBarProvider),
);
return (
<SessionProvider>
<SideBarProvider>
{children}
</SideBarProvider>
</SessionProvider>
);
}
Now it is working just as intended!
3 Replies
European sprat
needs to be a client component and you can't use window in useState like that because it gets prerendered on the server where there's no window, you'll need to set it in useEffect
MaKTaiLOP
It is a client component. I tried using useEffect but there is this weird initial state when refreshing the page where the sidebar is either opened or closed when it should be otherwise.
MaKTaiLOP
@European sprat Ok, I figured out how to fix this. It was a combination of two things:
1. First I moved the useState logic inside a useLayoutEffect (useEffect wasn't enough)
2. Second I used the dynamic library from "next/dynamic" inside the providers.tsx to import the SideBarContext:
Now it is working just as intended!
1. First I moved the useState logic inside a useLayoutEffect (useEffect wasn't enough)
useLayoutEffect(() => {
if (window.innerWidth >= 1280) {
setSideBarOpen(true);
}
}, []);
2. Second I used the dynamic library from "next/dynamic" inside the providers.tsx to import the SideBarContext:
"use client";
import { SessionProvider } from "next-auth/react";
import dynamic from "next/dynamic";
export default function Providers({ children }: { children: React.ReactNode }) {
const SideBarProvider = dynamic(() =>
import("@/contexts/SideBarContext").then((mod) => mod.SideBarProvider),
);
return (
<SessionProvider>
<SideBarProvider>
{children}
</SideBarProvider>
</SessionProvider>
);
}
Now it is working just as intended!
Answer