useMap

Introduction

useMap 훅은 React 애플리케이션에서 키-값 쌍의 맵 상태를 효과적으로 관리하기 위한 커스텀 훅입니다. 이 훅을 사용하면 맵 상태를 손쉽게 생성하고 조작할 수 있습니다.

기능:

  1. 맵 상태 초기화: 초기 상태를 설정할 수 있으며, 빈 맵이나 키-값 쌍의 배열로 초기화할 수 있습니다.

  2. 세터 액션 제공: set을 사용하여 새로운 키-값 쌍을 추가하거나 기존 키의 값을 갱신할 수 있습니다.

  3. 일괄 설정 액션: setAll을 사용하여 주어진 맵이나 키-값 쌍의 배열로 맵 상태를 한 번에 설정할 수 있습니다.

  4. 키 제거 액션: remove를 사용하여 주어진 키에 해당하는 항목을 맵에서 제거할 수 있습니다.

  5. 맵 초기화 액션: reset을 사용하여 맵 상태를 초기화하여 빈 맵으로 만들 수 있습니다.

Example

import React from "react";

// useMap 훅을 사용한 컴포넌트 정의
const MapComponent: React.FC = () => {
  // useMap 훅을 사용하여 맵 상태와 액션을 초기화
  const [map, mapActions] = useMap<string, number>([
    ['하나', 1],
    ['둘', 2],
    ['셋', 3],
  ]);

  // 새로운 키-값 쌍을 추가하는 함수
  const handleSet = () => {
    mapActions.set('넷', 4);
  };

  // 맵에서 특정 키를 제거하는 함수
  const handleRemove = () => {
    mapActions.remove('둘');
  };

  // 맵을 초기화하는 함수
  const handleReset = () => {
    mapActions.reset();
  };

  return (
    <div>
      <h2>맵 컴포넌트</h2>

      <p>현재 맵 상태: {JSON.stringify([...map])}</p>

      <button onClick={handleSet}>'넷': 4 추가</button>
      <button onClick={handleRemove}'둘' 제거</button>
      <button onClick={handleReset}>맵 초기화</button>
    </div>
  );
};

export default MapComponent;
  • useMap 훅을 사용하여 map 상태와 mapActions 액션을 초기화합니다.

  • 컴포넌트는 현재 맵 상태를 렌더링합니다.

  • set, remove, reset 액션의 사용법을 보여주기 위한 버튼이 있습니다.

  • handleSet, handleRemove, handleReset 함수는 각각 해당 액션을 사용하여 맵 상태를 수정합니다

Hook

import type { Dispatch, SetStateAction } from "react";
import { useCallback, useEffect, useState } from "react";

type SetValue<T> = Dispatch<SetStateAction<T>>;

interface Options {
  initialSave: boolean;
}

const isServer = typeof window === "undefined";

/**
 * 이 훅은 로컬스토리지를 관리하는 커스텀 훅 입니다.
 * 기본적으로 로컬스토리지의 값을 읽기, 저장, 삭제, 업데이트 등의 기능을 제공하고 있습니다.
 * @param {string} key - 로컬 스토리지의 Key
 * @param {T} initialValue - 로컬 스토리지의 초깃값
 * @param {Options} options - 훅에서 쓰이는 옵션 값
 * @returns {()=>{}} read(읽기), save(저장), remove(삭제), update(업데이트 마운트)
 */

/**
 *
 * @param options
 * @description options의 기능에 대한 설명
 * initialSave : 스토리지에 저장된 값이 없을 경우, 초기값을 저장할 것인지 말 것인지 type : boolean
 */
export default function useLocalStorage<T>(key: string, initialValue: T | (() => T), options: Options = { initialSave: false }) {
  const [storedValue, setStoredValue] = useState<T>(initialValue);

  const isStorageEvent = (event: StorageEvent | CustomEvent): event is StorageEvent => {
    return "key" in event;
  };

  /** 로컬 스토리지 읽기 */
  const readValueFromLocalStorage = (): T => {
    const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue;

    // SSR에서 발생할 수 있는 오류 방지하기 위해 설정. window 객체가 존재하지 않을때, localStorage에 접근 하는 것을 방지
    if (isServer) {
      return initialValueToUse;
    }

    try {
      const rawData = window.localStorage.getItem(key) as T;

      /** 값이 없을 경우 */
      if (typeof rawData === "object" && rawData === null && options.initialSave) {
        save(initialValueToUse);
        return initialValueToUse;
      }

      return rawData ? rawData : initialValueToUse;
    } catch {
      console.warn(`로컬 스토리지를 가져오는 것에 실패하였습니다. 키 : "${key}", 값 : error  `);
      return initialValueToUse;
    }
  };
  // read함수 메모이제이션
  const read = useCallback<() => T>(() => {
    return readValueFromLocalStorage();
  }, []);

  useEffect(() => {
    setStoredValue(read());
  }, [key]);

  /** 로컬 스토리지 읽기 */

  /** 로컬 스토리지 저장 */
  const saveValueFromLocalStorage = (value: T) => {
    if (isServer) {
      console.warn("브라우저 환경에서만 사용 가능합니다. 서버환경에서 사용이 불가능합니다.");
      return undefined;
    }

    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
      return value;
    } catch (error) {
      if (error instanceof DOMException && error.name === "QuotaExceededError") {
        console.error("실패 : 로컬 스토리지 용량이 부족합니다.");
      } else {
        console.warn(`로컬 스토리지에 값을 저장하는 데 실패했습니다. 키: "${key}"`);
      }
    }
  };

  const save: SetValue<T> = useCallback(async (value: T) => {
    return await saveValueFromLocalStorage(value);
  }, []);
  /** 로컬 스토리지 저장 */

  /** 로컬 스토리지 변화에 따른 상태관리 감지 */
  const update = useCallback(
    (event: StorageEvent | CustomEvent) => {
      if (isStorageEvent(event) && event.key !== key) return;
      setStoredValue(read());
    },
    [key, read]
  );
  /** 로컬 스토리지 변화에 따른 상태관리 감지 */

  /** 로컬 스토리지 삭제 */
  const remove = () => {
    if (isServer) {
      console.warn("현재 브라우저 환경이 아닙니다.");
      return key;
    }

    try {
      window.localStorage.removeItem(key);
      setStoredValue(undefined);
    } catch (error) {
      console.warn(`로컬 스토리지에서 항목을 삭제하는 데 실패했습니다. 키: "${key}"`);
      return;
    }
  };

  return { read, save, remove, update };
}

Last updated