⛷️

zustandでstoreをストレージ管理

2025/01/27に公開

最近ReactNativeでzustandを使ってみました
簡単に状態管理ができて便利ですね
中でもストレージ管理が簡単でいいなと思いました

zustandで認証トークンをstore管理

storeをストレージ管理する例として、認証トークンを永続化することを考えます
以下はストレージ導入する前のコードです
認証APIを実行し、返り値(token)をstoreで管理します

import { create } from 'zustand';

type AuthState = {
  token: string | null;
  loading: boolean;
  login: ({ id, password }: { id: string; password: string }) => void;
  logout: () => void;
};

export const useAuth = create<AuthState>((set, get) => ({
  token: null,
  loading: false,
  login: async ({ id, password }) => {
    set({ loading: true });
    await fetch('api/login', {
      method: 'POST',
      body: JSON.stringify({ id, password }),
    })
      .then((res) => res.json())
      .then((data) => set({ token: data.token }))
      .finally(() => set({ loading: false }));
  },
  logout: async () => {
    set({ loading: true });
    await fetch('api/logout', {
      method: 'POST',
      body: JSON.stringify({ token: get().token }),
    })
      .then(() => set({ token: null }))
      .finally(() => set({ loading: false }));
  },
}));

tokenをストレージに保存

storeデータをストレージに保存し、アプリ起動時に前回のデータを使いたい場合があります
これを実装する際はストレージのロード/保存処理等を書く必要はなく、設定の追記だけで目的の処理を行えます
具体的に、zustand/middleware@react-native-async-storage/async-storageをインストールし、以下の様に修正を加えるとtokenだけ永続化され、アプリ起動時に前回のtokenが自動的にロードされます

  import { create } from 'zustand';
+ import { persist, createJSONStorage } from 'zustand/middleware';
+ import AsyncStorage from '@react-native-async-storage/async-storage';

  type AuthState = {
    token: string | null;
    loading: boolean;
    login: ({ id, password }: { id: string; password: string }) => void;
    logout: () => void;
  };

  export const useAuth = create<AuthState>()(
+   persist(
      (set, get) => ({
        token: null,
        loading: false,
        login: async ({ id, password }) => {
          set({ loading: true });
          await fetch('api/login', {
            method: 'POST',
            body: JSON.stringify({ id, password }),
          })
            .then((res) => res.json())
            .then((data) => set({ token: data.token }))
            .finally(() => set({ loading: false }));
        },
        logout: async () => {
          set({ loading: true });
          await fetch('api/logout', {
            method: 'POST',
            body: JSON.stringify({ token: get().token }),
          })
            .then((res) => set({ token: null }))
            .finally(() => set({ loading: false }));
        },
      }),
+     {
+       name: 'auth-storage',
+       partialize: (state) => ({ token: state.token }),
+       storage: createJSONStorage(() => AsyncStorage),
+     }
+   )
  );

上記のstoreデータについて、token以外の値(loadingなど)は永続管理したくありません
この場合、上記のpartialize: (state) => ({ token: state.token })のように永続化したい値を指定してあげるとその値のみアプリ起動時にロードされるようです

所感

  • 同じデータをstoreとストレージの両方で管理したい場合はよくあるため、上記のように一緒に定義してストレージのロード/更新のコードを省略できるのはとても楽ですね
  • zustandのファイルはsrc/hooks/useAuth.tsみたいに他のhooksと一緒にしてしまっていいのかな
  • 変数を更新するちょっとした関数が多くなった場合はファイル分割ってできるんですかね

Discussion