React Native + Unstated Nextで手軽に状態管理する

September 22nd, 2021

最近個人開発でMuscle Note(マッスルノート)というiOSアプリをReact Nativeで作りました。

その際の状態管理にRedux等の有名ライブラリではなく、Unstated Nextという超軽量状態管理ライブラリを使ってみたところ開発体験がとても良かったので今回ぼくが用いた手法をシェアします。


Unstated Next

Unstated Nextは一言で言えば「グローバルな状態をhooksを用いて管理する」ライブラリです。

Unstated Nextの中身はCreateContexthooksをさらにContainerという独自の概念で包括しただけで、ContainerProviderで子コンポーネントをラップすればそれ以下の孫・ひ孫コンポーネント問わずどこからでもhooksの値にアクセス・更新することができます。

具体的な使用方法ですが、今回はUserという独自のEntityを定義し、そちらを用いて紹介します。

まずは適当にUser型の定義と、UserinitialStateを定義します。

src/entity/user.ts

export type User = {
  uid: string;
  name: string;
}

export const initialUser: User = {
  uid: uuid.v4() as string;
  name: 'hoge太郎'
}

次に、上記で定義したUserを更新するカスタムhooksを作り、それをunstated nextで提供されるcreateContainerという関数でラップします。

src/container/userContaienr.ts
const useUser = () => {

  const [user, setUser] = useState<User>(initialUser);

  const updateUser = (val: Partial<User>) => {
    setUser({ ...user, ...val });
  };

  return { user, setUser, updateUser };
}

export const UserContainer = createContainer(useUser);

最後に、上記で作成したUserContainerで、App.tsxをラップします。

App.tsx

export default function App() {
  return (
      <UserContainer>
        <UserComponent />
      </UserContainer>

  );
}

UserComponent内では以下のようにして呼び出します。

src/UserComponent.tsx

const UserComponent: FC<Props> = prosp => {

  const { user, updateUser} = useContainer(UserContainer)

  return (
    ...省略
    )
}

これだけです。超簡単です。

Unstated Nextを永続化する

まずは永続層を定義します。今回はreact-native-storageというライブラリを用いて以下のように宣言します。

src/storage.ts

import Storage from 'react-native-storage';
import { AsyncStorage } from 'react-native';

export const storage = new Storage({
  size: 10000,
  storageBackend: AsyncStorage,
  defaultExpires: null,
  enableCache: true,
});

次に状態の更新ですが、useEffectdpsuserプロパティを付与することで、userに変更があった際に永続層に保存されるようにします。

src/container/userContaienr.ts

useEffect(() => {
    storage.save({
        key: 'user',
        data: user,
      });
  }, [user]);

これで、グローバルhooksが更新される度に永続層も同期的に更新されます。 また、アプリ起動時の永続層からのデータの読み込みも、同じくuseEffectを用いdpsに空配列を渡すことでアプリ起動時だけ呼ばれる処理を定義します。

src/container/userContaienr.ts

useEffect(() => {
    storage.load({ key: 'user' }).then(v => {
        setUser(v);
    });
  }, []);

終わり。

ps

こちらが上記手法を用いて作ったMuscle Note(マッスルノート)というアプリです。ぜひ使っていただけると嬉しいです。