個人的に最近よく使っている 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 にデータを半永続化することができます。
ということで、メモでした。