Next.js Discord

Discord Forum

localStorage is not defined, 'use client' doesn't fix it

Answered
Spruce Grouse posted this in #help-forum
Open in Discord
Spruce GrouseOP
My code looks as follows:

import { proxy, subscribe } from "valtio";

const state = proxy(
JSON.parse(localStorage.getItem("state")) {
...data_is_here
);

subscribe(state, () => {
localStorage.setItem("state", JSON.stringify(state));
});

export default state;


I'm using valtio as my state manager and I want to persist the state, but I get the following error:

⨯ src\store\index.js (7:13) @ localStorage
⨯ ReferenceError: localStorage is not defined
at eval (./src/store/index.js:9:77)
at (ssr)/./src/store/index.js (....next\server\app\sources\page.js:327:1)
at webpack_require (....next\server\webpack-runtime.js:33:42)
at eval (./src/app/sources/page.jsx:10:64)
at (ssr)/./src/app/sources/page.jsx (....next\server\app\sources\page.js:162:1)
at webpack_require (....next\server\webpack-runtime.js:33:42)
at JSON.parse (<anonymous>)
6 | const state = proxy(
7 | JSON.parse(localStorage.getItem("state"))
{
| ^
8 | ...data_is_here


the file structure is as follows (using app router):
/src/store/indexjs

I've tried using 'use client' as well as {typeof window !== 'undefined'} and even played with the configs trying to exclude the file from building so that it can just be skipped (which I'm not sure even worked).

The way I'm using this code/file is I have the store defined and I import it into whichever file I use, I made sure to 'use client' wherever I use the global state.

This is all the information, I look forwards to your assistance.
Answered by Arinji
@Spruce Grouse use client components aren't always on the client, they get rendered once on the server, hence local storage is not defined


Use a useEffect in which you get localStorage data, if you want to not show a ui, you can use state to conditionally render
View full answer

12 Replies

Spruce GrouseOP
"use client" import { useEffect } from "react" import { subscribe } from "valtio"; import state from "@/store" export default function StateLoader({ children }) { useEffect(() => { const localState = JSON.parse(localStorage.getItem("state")) if (localState !== undefined) { // can't change state directly since it's imported i think state.somedata = localState.somedata state.somedata2 = localState.somedata2 } subscribe(state, () => { localStorage.setItem("state", JSON.stringify(state)); }); }, []) return ( <>{children}</> ) }


I have thought of this work around but the initial state before the localStorage is accessible would be visible for a moment before it updates. If there is a way to avoid it it would be best.
@Spruce Grouse use client components aren't always on the client, they get rendered once on the server, hence local storage is not defined


Use a useEffect in which you get localStorage data, if you want to not show a ui, you can use state to conditionally render
Answer
Spruce GrouseOP
Thank you for your help
Spruce GrouseOP
My use case can’t implement conditional rendering.

A component renders based on a value in the state, there is a default value for it, but when the user changes it next time he visits the site it should be the same component, so I saved it in local storage.
A component is always rendered the state help me choose which one, if there’s a default value I can’t conditionally render since it could be the original default value
So just don't do a default value
Just hide the ui till you read local storage.. if you don't see anything show the default... Else show the local storage data
In react you can't instantly read local storage so that's the only way
Spruce GrouseOP
I’ve figured out a solution just now 😄

I appreciate your time, thank you.
It’s similar to what you said
Alright, mark a solution:)
Original message was deleted
Follow this