By Paul Scanlon

Using a useLocalStorage hook and rehydration

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

The Problem

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”.

The Solution

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];
}
Hey!

Leave a reaction and let me know how I'm doing.

  • 0
  • 0
  • 0
  • 0
  • 0
Powered byNeon