By Parham Negahdar

useLocalStorage: hooks are nice

react-use has a cool useLocalStorage module that allows you to do what it sounds like it allows you to do - read values into state from the localStorage. You can also find the generally straight forward source for it here.

This lib mostly handles the serialization but I wanted to add a watcher to the storage key as well. I generally don't recommend binding your state to localStorage like this but I needed to and it let me quickly implement some cool features for CloudSynth Inbox.

I went on to extend that library for our use cases, but I wanted to write out a simplified version. I'm really enjoying the encapsulation provided by hooks in react in typescript. Its very simple to write a decent utility like useLocalStorage.

function useLocalStorageLite<T>(key: string) {
    // pull the initial value from local storage if it is already set
    const [state, setState] = useState<T | null>(() => {
        const exValue = localStorage.getItem(key)
        if (exValue) {
            return JSON.parse(exValue) as T
        }
        return null
    })

    // save the new value when it changes
    useEffect(() => {
        localStorage.setItem(key, JSON.stringify(state))
    }, [state])

    // memoize a storage watcher callback back because everything in hooks should be memoized
    const storageWatcher = useCallback(
        (e: StorageEvent) => {
            if (e.newValue) {
                // update ours if we
                setState((currState) => {
                    const newDat = JSON.parse(e.newValue || "null")
                    return newDat == state ? newDat : currState
                })
            }
        },
        [state]
    )

    // install the watcher
    useEffect(() => {
        window.addEventListener("storage", storageWatcher)
        // stop listening on remove
        return () => {
            window.removeEventListener("storage", storageWatcher)
        }
    }, [state])

    return { state, setState }
}

Now, we're missing a few things like error catching and a correct equality check but this is pretty neat and clean. I returned to frontend development recently and its nice to see these hooks cleaning things up quite a bit. It's very easy (as you should) to extend the existing react-use implementation with a few lines too. It's also nice to see the emergence of headless UI libs enabled by hooks.

There are definitely going to be some annoyances like a ton of useEffects and useCallbacks and you might need useWhyDidYouUpdate at some point but the complexity seems inherent to the problem.

So overall, hooks are nice.