SCALE — Build Lab
開発パターン · TYPESCRIPT PATTERN

localStorage ストアパターン

CATEGORY開発パターン TYPETypeScript Pattern EFFORT60〜180分 DIFFICULTY
PRIMARY CODE
ts
// 型安全 localStorage ストア(subscribe / notify 対応)
type Listener<T> = (value: T) => void;

export function createLocalStore<T>(key: string, initial: T) {
  const listeners = new Set<Listener<T>>();

  function read(): T {
    try {
      const raw = localStorage.getItem(key);
      return raw ? (JSON.parse(raw) as T) : initial;
    } catch {
      return initial;
    }
  }

  function write(value: T) {
    localStorage.setItem(key, JSON.stringify(value));
    listeners.forEach((l) => l(value));
  }

  function subscribe(l: Listener<T>) {
    listeners.add(l);
    return () => listeners.delete(l);
  }

  // 別タブ同期
  if (typeof window !== 'undefined') {
    window.addEventListener('storage', (e) => {
      if (e.key === key) listeners.forEach((l) => l(read()));
    });
  }

  return { read, write, subscribe };
}

// 使い方:
// const store = createLocalStore<{ theme: 'light' | 'dark' }>('settings', { theme: 'light' });
// store.subscribe((v) => console.log(v));
// store.write({ theme: 'dark' });
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • ダッシュボードのUI状態
  • フォームのドラフト保存

localStorage ストアパターン

:LiTarget: 用途

localStorage に型安全にデータを保存・読込みするパターン。subscribe/notify 含む。

:LiSparkle: 特徴

  • 型安全
  • subscribe/notify
  • JSON自動シリアライズ
  • デフォルト値対応

:LiCode: コード(コピペ用)

// 型安全 localStorage ストア(subscribe / notify 対応)
type Listener<T> = (value: T) => void;

export function createLocalStore<T>(key: string, initial: T) {
  const listeners = new Set<Listener<T>>();

  function read(): T {
    try {
      const raw = localStorage.getItem(key);
      return raw ? (JSON.parse(raw) as T) : initial;
    } catch {
      return initial;
    }
  }

  function write(value: T) {
    localStorage.setItem(key, JSON.stringify(value));
    listeners.forEach((l) => l(value));
  }

  function subscribe(l: Listener<T>) {
    listeners.add(l);
    return () => listeners.delete(l);
  }

  // 別タブ同期
  if (typeof window !== 'undefined') {
    window.addEventListener('storage', (e) => {
      if (e.key === key) listeners.forEach((l) => l(read()));
    });
  }

  return { read, write, subscribe };
}

// 使い方:
// const store = createLocalStore<{ theme: 'light' | 'dark' }>('settings', { theme: 'light' });
// store.subscribe((v) => console.log(v));
// store.write({ theme: 'dark' });

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加