More of my content can be found on the Gatsby Blog
useLocalStorage-image

useLocalStorage

Date published: 2-Jan-2022
1 min read / 251 words
Author: Paul Scanlon
JavaScript
React

Recently I ran into an issue using the useLocalStorage hook from useHooks.com -- which is brills by the way!

The issue I was seeing was weird, React would mess up the way my DOM nodes were ordered resulting in some very odd looking layouts. The problem ocurred after i'd used useLocalStorage to "set state" and I then refreshed the page.

To give you some context, if you pop over to https://gatsbyconf.com/ and register, you'll be given a super duper raffle code. If users didn't write this code down I was concerned they'd lose it. One solution is to persist the state of the UI so even if a user closes their browser tab, the next time they come back to the site the raffle code would still be displayed.

This worked fine if you followed the journey, E.g register, let React re-render and show the raffle code, but it messed up if you refreshed the page.

The reason is, the useLocalStorage hook has a different initialValue on refresh than it does on "set state".

Luckily Ward from Gatsby's engineering team came up with the following solution and i'll do my best to explain it.

  • The storedValue is set to the initialValue and the try / catch has been moved to a useEffect
  • The useEffect is set to run "on mount" and grabs any localStorage values and then uses them to setStoredValue

This results in React re-rendering when both the value is set manually and when the component mounts.

Here's the diff, hope it helps, of if you're looking for the src here's a Gist

import { useState } from "react";
const useLocalStorage(key, initialValue) => {
+ const [storedValue, setStoredValue] = useState(initialValue)
- const [storedValue, setStoredValue] = useState(() => {
- try {
- const item = window.localStorage.getItem(key);
- return item ? JSON.parse(item) : initialValue;
- } catch (error) {
- console.log(error);
- return initialValue;
- }
- });
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.log(error);
}
};
+ useEffect(() => {
+ try {
+ const item = window.localStorage.getItem(key)
+ setStoredValue(item ? JSON.parse(item) : initialValue)
+ } catch (error) {
+ console.log(error)
+ return setStoredValue(initialValue)
+ }
+ }, [])
return [storedValue, setValue];
}

Reactions

Loading...

Newsletter

This is a sign-up to Queen Raae's Gatsby Newsletter because I don't really like people, or emails. She'll let you know when I have something new to share!

Pay what you want

If you've enjoyed this post you can make a secure contributionPowered by Stripe - blurple
$