[typescript] useLocalStorage 커스텀 훅 만들어서 쓰기

2024. 11. 25. 13:00
반응형

// useLocalStorage.ts

import { useState, useEffect, useCallback } from 'react';

function useLocalStorage<T>(key: string, initialValue?: T) {
  const [storedValue, setStoredValue] = useState<T | undefined>(() => {
    if (typeof window === 'undefined') {
      return initialValue;
    }
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(`Error reading localStorage key “${key}”:`, error);
      return initialValue;
    }
  });

  // Update localStorage when the value changes
  const setValue = useCallback(
    (value: T | ((prev: T | undefined) => T)) => {
      try {
        const valueToStore =
          value instanceof Function ? value(storedValue) : value;
        setStoredValue(valueToStore);
        if (typeof window !== 'undefined') {
          window.localStorage.setItem(key, JSON.stringify(valueToStore));

          // Trigger a custom storage event for React context sync
          window.dispatchEvent(
            new StorageEvent('storage', {
              key,
              newValue: JSON.stringify(valueToStore),
            }),
          );
        }
      } catch (error) {
        console.error(`Error setting localStorage key “${key}”:`, error);
      }
    },
    [key, storedValue],
  );

  const removeValue = useCallback(() => {
    try {
      if (typeof window !== 'undefined') {
        window.localStorage.removeItem(key);
      }
      setStoredValue(undefined);
    } catch (error) {
      console.error(`Error removing localStorage key “${key}”:`, error);
    }
  }, [key]);

  // Listen for storage changes
  useEffect(() => {
    const handleStorageChange = (event: StorageEvent) => {
      if (event.key === key) {
        try {
          const newValue = event.newValue
            ? JSON.parse(event.newValue)
            : undefined;
          setStoredValue(newValue);
        } catch (error) {
          console.error(`Error parsing localStorage key “${key}”:`, error);
        }
      }
    };

    window.addEventListener('storage', handleStorageChange);

    return () => {
      window.removeEventListener('storage', handleStorageChange);
    };
  }, [key]);

  return { storedValue, setValue, removeValue } as const;
}

export default useLocalStorage;

 

 

// 외부에서 가져다 쓰는 컴포넌트 예시

...

const { setValue, storedValue } = useLocalStorage<ValuesType>('sell');

const handleClick = (input : string) => {
  setValue((prev) => ({ ...prev, phoneNumber: input }));
};

 

 

hook 안의 코드에서 EventListener와 dispatchEvent를 달아놨기 때문에,

localStorage 값이 업데이트 됐을 때 화면단도 같이 씽크를 맞춰 잘 바뀐다.

 

 

반응형

BELATED ARTICLES

more