React Native + zustand で AsyncStorage に永続化する

zustand に格納した state をそのまま AsyncStorage に保存します。

以前は TypeORM + SQLite でデータの永続化を行っていたのですが、わざわざORマッパーを書くよりも、 state の配列をそのまま出し入れできた方が楽でした。

インストール

zustand と AsyncStorage をインストールします。

shell
npm install zustand
npx expo install @react-native-async-storage/async-storage

扱うデータの型を定義する

Tag.ts
export interface Tag {
  id: string;
  name: string;
}

Store を作成

tagStore.ts
import { type Tag } from '../interfaces/tag';
import { create, type StateCreator } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

export interface TagStore {
  tags: Tag[];
  setTags: (tags: Tag[]) => void;
}

const tagStore: StateCreator<TagStore> = (set, get) => ({
  tags: [
    {
      id: '1fe47211-161e-481d-a23d-36230731d074',
      name: '家族',
    },
    {
      id: 'eb1b6e54-9c8e-426b-b95c-817ed634fe25',
      name: '友達',
    },
    {
      id: 'c9ea5882-329d-4a41-8b0b-7a7d17ec574b',
      name: '恋人',
    },
    {
      id: '80a2c2e9-fe59-4a75-a6df-3b4dc1b09e36',
      name: '同僚',
    },
    {
      id: 'd8b47490-10dc-476b-bb30-7d15161ea805',
      name: 'その他',
    },
  ],
  setTags: (tags: Tag[]) => {
    set(() => ({ tags }));
  },
});

export const useTagStore = create(
  persist(tagStore, {
    name: 'tag-storage',
    storage: createJSONStorage(() => AsyncStorage),
  })
);

データを取り出す

コンポーネント内で以下のようにしてデータを取り出せます。

TagRegisterScreen.tsx
const { tags } = useTagStore();

データを更新する

TagRegisterModal.tsx
const TagRegisterModal = (props: TagRegisterModalProps) => {
  const { tags, setTags } = useTagStore();
  const [tagName, setTagName] = useState('');

  const onPressAddTag = () => {
    const newTag: Tag = {
      id: uuidv4(),
      name: tagName,
    };
    setTags([...tags, newTag]);
    setTagName('');
    props.setModalVisible(!props.modalVisible);
  };

  return (
// 省略

これだけで端末を再起動しても失われることなくデータを保存できます。