DEV Community

Daniel Bellmas
Daniel Bellmas

Posted on

useStorage-Custom Hook in Next.js

If you're using Next.js you know that it doesn't get along with localStorage (or any storage for that matter).
That's because the storages are located under the global object windows, which is equal to undefined on the server, so we have to explicitly tell Next.js what to run in the server and what on the client.

First, I added a util that indicates if we are in SSR (server side rendering):

export const isSsr = typeof window === 'undefined';
Enter fullscreen mode Exit fullscreen mode

The hook 🪝

import { useState, useEffect } from 'react';
import { isSsr } from '@/utils/isSsr';

export const getStorage = (storage, key) => JSON.parse(storage.getItem(key));

export const setStorage = (storage, key, newValue) => storage.setItem(key, JSON.stringify(newValue));

const useStorage = (storageType, key, initialValue) => {
  if (isSsr) return [initialValue];

  const storageName = `${storageType}Storage`;
  const storage = window[storageName];

  const [value, setValue] = useState(getStorage(storage, key) || initialValue);

  useEffect(() => {
    setStorage(storage, key, value);
  }, [value]);

  return [value, setValue];
};

export default useStorage;
Enter fullscreen mode Exit fullscreen mode

A brief rundown

  • We have 2 functions getStorage and setStorage that are responsible for getting and parsing and setting and stringifying the data respectively.

  • Before writing the logic that uses the window object I told Next.js to return the initial value.

  • Every time the value changes the hook will update the chosen storage.

How to use


const LOCAL_STORAGE_KEY = 'filters';
const initialStateFilters = { isExpended: true };

const [filters, setFilters] = useStorage('local', LOCAL_STORAGE_KEY, initialStateFilters);

// The value
const { isExpended } = filters;

// Setting the value
const handleToggle = newIsExpended => setFilters({ ...filters, isExpended: newIsExpended });
Enter fullscreen mode Exit fullscreen mode

You're maybe wondering why I'm using an object as the value of the data, it is simply for scalability reasons.
In the future we'll probably want to add more filters, so instead of creating a hook for each one we'll have them all under the same key.

Thank you for reading!

Discussion (0)