JavaScriptによるマルチプラットフォーム状態管理の設計と最適化

複数のプラットフォーム(Web、モバイル、デスクトップ)で動作するアプリケーションにおいて、状態の一貫性と効率的な同期は重要な課題です。各環境の実行コンテキストが異なるため、データの隔離と共有のバランスを取る必要があります。

中心化ストアによる統一管理

状態を単一の信頼できるソースとして扱うことで、複雑な同期処理を回避できます。以下は、シンプルなカウンターストアの例です:

// 状態定義
const storeState = { counter: 0 };

// 更新ロジック
function updateCounter(action, state = storeState) {
  switch (action.cmd) {
    case 'ADD':
      return { ...state, counter: state.counter + action.step };
    case 'SUBTRACT':
      return { ...state, counter: state.counter - action.step };
    default:
      return state;
  }
}

リアクティブ更新の進化

手動での再描画から、依存関係を自動追跡するリアクティブモデルへ移行しています。代表的なライブラリには次のような特徴があります:

  • Zustand:軽量でHookベース。React Nativeとの相性が良い。
  • Jotai:原子的な状態分割により、再レンダリング範囲を最小化。
  • Valtio:Proxyベースで直感的なミューテーションを可能に。
ライブラリ同期方式推奨用途
Zustandサブスクリプション中規模アプリ
Redux Toolkitミドルウェア駆動大規模ビジネスロジック
BroadcastChannel APIブラウザタブ間通信マルチウィンドウ同期
graph LR A[状態変更] --> B{永続化要否} B -->|Yes| C[ストレージ保存] B -->|No| D[メモリ更新] C --> E[他インスタンス通知] E --> F[全端末同期]

Reduxのクロスプラットフォーム最適化

プラットフォーム差異を吸収するため、アクションの正規化ミドルウェアを導入します:

const normalizeMiddleware = next => action => {
  const standardized = {
    type: action.type,
    data: transformPayload(action.payload)
  };
  return next(standardized);
};

パフォーマンス向上策:

  • メモ化セレクター(reselect)で重複計算を削減
  • 非同期アクションにキャッシング機構を追加
  • 動的Reducer注入で初期ロードを軽量化

MobXによる細粒度リアクティブ管理

観測可能なプロパティとアクションを明示的に宣言することで、副作用を制御します:

class ProfileStore {
  profile = { name: '', age: 0 };

  constructor() {
    makeAutoObservable(this);
  }

  setName = (value) => {
    this.profile.name = value;
  };
}
最適化手法適用シーン効果
reaction分割高頻度更新★★★
computedキャッシュ派生値計算★★☆

Zustandによる軽量ステートシェアリング

プラットフォーム固有のストレージを抽象化して、一貫したAPIを提供します:

const useSession = create(
  persist(
    (set) => ({
      token: '',
      setToken: (val) => set({ token: val }),
      clear: () => set({ token: '' })
    }),
    {
      key: 'session',
      storage: {
        getItem: async (name) => {
          return isNative ? await AsyncStorage.getItem(name) : localStorage.getItem(name);
        },
        setItem: async (name, value) => {
          return isNative ? await AsyncStorage.setItem(name, value) : localStorage.setItem(name, value);
        }
      }
    }
  )
);

Piniaとミニプログラムの連携

Vueエコシステムとミニプログラム間で状態を橋渡しする方法:

const appStore = defineStore('app', {
  state: () => ({ theme: 'light' }),
  actions: {
    toggleTheme() {
      this.theme = this.theme === 'light' ? 'dark' : 'light';
      if (typeof wx !== 'undefined') {
        wx.setStorageSync('THEME_PREF', this.theme);
      }
    }
  }
});

カスタム状態遷移機械の設計

ビジネスロジックを状態マシンとしてモデリングすることで、可読性と保守性を向上させます:

class WorkflowEngine {
  #currentState = 'IDLE';
  #transitions = {};

  registerTransition(from, event, to) {
    if (!this.#transitions[from]) this.#transitions[from] = {};
    this.#transitions[from][event] = to;
  }

  dispatch(event) {
    const next = this.#transitions[this.#currentState]?.[event];
    if (next) {
      this.#currentState = next;
      return true;
    }
    return false;
  }
}
ユースケース開始状態トリガー終了状態
決済フローAUTH_PENDINGPAYMENT_OKCOMPLETED
ワークフローDRAFTSUBMITREVIEWING

リアルタイム同期のPub/Sub実装

WebSocketやRedis Pub/Subを利用して、複数クライアント間で即時同期を実現:

class SyncHub {
  #channels = new Map();

  subscribe(channel, handler) {
    if (!this.#channels.has(channel)) {
      this.#channels.set(channel, []);
    }
    this.#channels.get(channel).push(handler);
  }

  publish(channel, payload) {
    const handlers = this.#channels.get(channel) || [];
    handlers.forEach(fn => fn(payload));
  }
}

オフライン時の状態復元

IndexedDBを使用して構造化データを永続化し、ネットワーク復帰時に自動同期:

async function initDB() {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open('AppCache', 1);
    req.onupgradeneeded = e => {
      const db = e.target.result;
      if (!db.objectStoreNames.contains('snapshots')) {
        db.createObjectStore('snapshots', { keyPath: 'key' });
      }
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = reject;
  });
}

競合解消戦略

最終書き込み勝ち(Last Write Wins)やベクトルクロックを用いた高度な整合性制御:

function mergeStates(local, remote) {
  if (local.timestamp > remote.timestamp) {
    return { ...local, synced: true };
  } else {
    return { ...remote, synced: true };
  }
}

状態シャーディングによるパフォーマンス改善

巨大なグローバルステートを機能単位で分割し、必要なコンポーネントのみ更新通知:

const modularStore = {
  auth: { user: null, isAuthenticated: false },
  ui: { sidebarOpen: true, notifications: [] },
  cache: { entities: {}, lastFetched: {} }
};

ProxyとWeakMapによる効率的監視

メモリリークを防ぎつつ、動的な依存追跡を実現:

const watchers = new WeakMap();

function reactive(target) {
  return new Proxy(target, {
    get(obj, prop) {
      trackDependency(obj, prop);
      return Reflect.get(obj, prop);
    },
    set(obj, prop, value) {
      const result = Reflect.set(obj, prop, value);
      triggerWatchers(obj, prop);
      return result;
    }
  });
}

function trackDependency(obj, prop) {
  // 現在のEffectを登録
}

function triggerWatchers(obj, prop) {
  const fns = watchers.get(obj)?.[prop] || [];
  fns.forEach(fn => fn());
}

フレームワーク非依存ミドルウェア

React、Vue、Svelteなど複数フレームワークに対応可能な共通インターフェース:

class UniversalStore {
  #state = {};
  #listeners = new Set();

  getState() { return this.#state; }

  setState(updater) {
    this.#state = typeof updater === 'function' ? updater(this.#state) : updater;
    this.#notify();
  }

  subscribe(listener) {
    this.#listeners.add(listener);
    return () => this.#listeners.delete(listener);
  }

  #notify() {
    this.#listeners.forEach(fn => fn(this.#state));
  }
}

エッジコンピューティングとの連携

5G/IoT時代における分散状態管理。工場や店舗のエッジノードでローカル処理を行い、クラウドと非同期同期:

  • MQTTプロトコルによる低遅延通信
  • KubeEdgeによるエッジクラスタ管理
  • イベントドリブンアーキテクチャとの統合

タグ: javascript 状態管理 Zustand MobX React

7月20日 19:32 投稿