なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

Recoil と Recoil Sync で LocalStorage にデータを保存したい

個人的に最近よく使っている React のステート管理ライブラリである Recoil と、外部ストレージ (DB や URL) などとステートを同期するライブラリ Recoil Sync を使って、 LocalStorage にデータを保存しようという記事です。

ということで、いつも通り準備。
Recoil Sync を使うので、パッケージマネージャー経由で入れます。
このとき、 eslint-plugin-import を使っている場合は、 @recoiljs/refine も追加してあげる必要があります。

$ yarn add recoil-sync

追加したら、まずは LocalStorage と同期するための RecoilSync コンポーネントを作成します。
基本的には Implementing a Store と同様の実装をすれば問題ありません。
今回の場合は、以下のようになると思います。

import React from "react";
import { ItemKey, RecoilSync, WriteInterface } from "recoil-sync";

import useLocalStorage from "../hooks/useLocalStorage"; // この実装をそのまま使用 → https://usehooks.com/useLocalStorage/

type Props = {
  children: React.ReactNode;
};

const RecoilSyncWithLocalStorage: React.FC<Props> = ({ children }) => {
  const [value, setValue] = useLocalStorage(
    "natsuneko.moe", // LocalStorage のキー (お好きにどうぞ)
    {
      /* 初期データを入れる */
    }
  );

  const read = (key: ItemKey) => {
    return value[key] ?? null;
  };

  const write = (state: WriteInterface) => {
    const { diff } = state;

    for (const [k, v] of diff) {
      value[k] = v;
    }

    setValue(value);
  };

  // StoreKey には、 RecoilSync 毎にユニークなキーを指定する
  return (
    <RecoilSync storeKey="LocalStorage" read={read} write={write}>
      {children}
    </RecoilSync>
  );
};

次に、これを RecoilRoot 直下などに突っ込みます。
この辺は、他の Recoil Sync を使う使い方と同じです。

// some component
return (
  <RecoilRoot>
    <RecoilSyncWithLocalStorage>{/* ... */}</RecoilSyncWithLocalStorage>
  </RecoilRoot>
);

あとは、 Recoil の atom で定義を入れてあげれば OK です。簡単ですね。

import { number, string } from "@recoiljs/refine";
import { atom } from "recoil";
import { syncEffect } from "recoil-sync";

type SomeState = {
  a: number;
  b: string;
};

const state = atom<SomeState>({
  key: "state",
  default: undefined, // ここは使われない (初期ステートにも Recoil Sync で引っぱってきたデータが使用される)
  effects: [
    syncEffect({
      storeKey: "LocalStorage", // 同期に使用する StoreKey、今回の場合は RecoilSyncWithLocalStorage で指定したキー
      itemKey: "state", // アイテム毎のキー, これが read および write で渡される
      refine: {
        a: number(),
        b: string(),
      },
    }),
  ],
});

とまぁ、こんな感じの実装で、簡単に LocalStorage にデータを半永続化することができます。
ということで、メモでした。