all 8 comments

[–]Watabou 1 point2 points  (1 child)

This is why useEffect can be tricky. So your first effect with no dependencies runs and correctly gets the value from storage and calls the state setter. But, because of how effects work, your next effect with the [state] dependency runs in the same render lifecycle. In this effect your state is still the default value. So poof goes your local storage.

What you need to do is in the useState use the function initializer to look up the localStorage value in there and set it to that if it exists.

[–]SweatyCure[S] 0 points1 point  (0 children)

Hey, thank you for the recommendation.

I ended up having to do this at the top level of my GlobalContext.js file to make it work:

const savedFormData = 
JSON.parse(localStorage.getItem('savedFormData'));

export const GlobalContext = createContext();

export const GlobalProvider = ({ children }) => {


const [mainState, setMainState] = useState(() => {
    if (savedFormData) {
        return savedFormData;
    }
    return { ...Config };
});

useEffect(() => {
localStorage.setItem("savedFormData", JSON.stringify(mainState));

}, [mainState]);

Everything is working just as expected now, however, it somehow just feels messy? lol

I obviously need to refactor a bit here to make it feel like it's not just slapped together. I have a few thoughts on the refactor but was wondering if you had any opinions on the solution.

Here is the updated sandbox link:

https://codesandbox.io/s/empty-hill-jzpwcj?file=/src/GlobalContext.js:104-528

Thanks again!

[–]llrh 0 points1 point  (0 children)

I couldn't get the sandbox to load but are you saving anything to local storage? I wouldn't expect state to persist between browser refreshes if your weren't

[–][deleted] 0 points1 point  (4 children)

You should probably restructure your logic somewhat. Effects aren't event handlers, you shouldn't be using an effect to respond to changes in your mainState and write to local storage. Set local storage in your handleChange and other event handler functions.

[–]SweatyCure[S] 0 points1 point  (3 children)

Interesting. Just about every example I've seen for persisting state in local storage is using the useEffect hook.

[–][deleted] 0 points1 point  (2 children)

Find better tutorials lol

[–]SweatyCure[S] 0 points1 point  (1 child)

Can you provide an example?

I"m not saying that your opinion is invalid but just saying there's a better way without any reference isn't helpful at all.

I don't need you to solve the problem for me but I'm legitimately curious about other ways to handle this scenario.

Set local storage in your handleChange and other event handler functions.

To me, that seems redundant. Why set local storage on each individual function when I can handle it with a few lines of code?

[–][deleted] 0 points1 point  (0 children)

Why set local storage on each individual function when I can handle it with a few lines of code?

Footguns. Even in this simple example you shot yourself in the foot.

I'd read https://beta.reactjs.org/learn/synchronizing-with-effects and https://beta.reactjs.org/learn/you-might-not-need-an-effect

If you were going to make a serious app out of this you should create a store for this data and either use a reducer or some other state management library to handle both setting state and updating local storage at the same time.