How To Use localStorage With State Initializer?
Unanswered
Australian Freshwater Crocodile posted this in #help-forum
Australian Freshwater CrocodileOP
Hello,
I am trying to grab data from localStorage and use it to change the text in a button. This system should detect if a user has clicked the button before and say "continue" instead of "start." I made a
Only this version results in no error, but it always flashes "Start" until the value is retrieved.
When I use
and remove the first
I understand this is due to the server pre-rendering the initial value of useState, a behaviour I do not want in this case.
Should I rethink this entire system?
I am trying to grab data from localStorage and use it to change the text in a button. This system should detect if a user has clicked the button before and say "continue" instead of "start." I made a
useUserHistory hook to wrap this behaviour.import { useEffect, useReducer, useState } from "react";
export type UserHistoryObject = {
[key: string]: string;
};
export interface UserHistory {
userHistory: UserHistoryObject;
setUserHistory: (pv: UserHistoryObject) => void;
getBoolean: (item: string) => boolean;
setBoolean: (item: string, value: boolean) => void;
getNumber: (item: string) => number;
setNumber: (item: string, value: number) => void;
getString: (item: string) => string;
setString: (item: string, value: string) => void;
clearUserHistory: () => void;
}
export default function useUserHistory(): UserHistory {
const [userHistory, setUserHistory] = useState({} as any);
useEffect(() => {
if (
localStorage.getItem("userHistory") &&
Object.keys(userHistory).length === 0
) {
setUserHistory(JSON.parse(localStorage.getItem("userHistory") as string));
}
}, []);
useEffect(() => {
if (
localStorage.getItem("userHistory") &&
Object.keys(userHistory).length === 0
)
return;
localStorage.setItem("userHistory", JSON.stringify(userHistory));
}, [userHistory]);
return {
userHistory,
setUserHistory,
getBoolean: (item) => userHistory?.[item] === "true" || false,
setBoolean: (item, value) => {
const newUserHistory = { ...userHistory };
newUserHistory[item] = String(value);
setUserHistory(newUserHistory);
},
getNumber: (item) => Number(userHistory?.[item]),
setNumber: (item, value) => {
const newUserHistory = { ...userHistory };
newUserHistory[item] = String(value);
setUserHistory(newUserHistory);
},
getString: (item) => userHistory?.[item],
setString: (item, value) => {
const newUserHistory = { ...userHistory };
newUserHistory[item] = value;
setUserHistory(newUserHistory);
},
clearUserHistory: () => {
setUserHistory({});
},
};
}Only this version results in no error, but it always flashes "Start" until the value is retrieved.
When I use
const [userHistory, setUserHistory] = useState(JSON.parse(localStorage.getItem("userHistory") || "{}"));and remove the first
useEffect, it seems to work, but I do get a error for localStorage not being defined.I understand this is due to the server pre-rendering the initial value of useState, a behaviour I do not want in this case.
Should I rethink this entire system?
2 Replies
Birman
Why not not render the button until the state has held a value
Australian Freshwater CrocodileOP
That was what I was thinking, but it would look kind of stupid to not render something basic until after hydration. I think this is probably the solution I am going to implement, but it is unfortunate there is not a better way to acomplish this.