【 Jotai 】atomWithStorage 保存した値が初回アクセスで取得できない時 ( getOnInit について )

2024年5月28日

普段 Vue 系をメインで使うことが多いため Store の Storage との同期には vuex-persistedstate や pinia-plugin-persistedstate を使っていたのですが、今回 React を使う機会がありその際同じ様に Storage との同期をする場合に使いやすそうなライブラリという事で Jotai の atomWithStorage を使ったのですがその際に標題の問題があったので残しておきます。

問題

簡略化していますが、以下の様に定義した Atom を使っていました。

const initialState = {
  id: undefined
};

export const targetAtom = atomWithStorage<Target>(
  "target",
  initialState,
);

以下の様に isInitialize される際に初期処理として target.id があれば何か処理を行うという様にしてましたが、ここで明らかに id に値が入っていた場合でも target.id が undefinedになっており if 以下の処理がされないという状況でした。

export default function Test() {
  const [isInitialized, setIsInitialized] = useState<boolean>(false);
  const [target, setTarget] = useAtom(targetAtom);  

if (!isInitialized) {
    if (target.id) {
      // do something
    }
    setIsInitialized(true);
  }

しかもそれ以降のアクセセスでは正しく target.id に保存されている値が取得できています。謎だったので調べたところ以下のような仕様らしいです。

getOnInit

こちら公式ドキュメントによると Atom 定義時に getOnInit の値を指定しないとデフォルトで false となり、初期化時には常に初期値 ( 今回の場合だと undefined ) が取得されるという仕様の様でした。

むしろ false の初期化時に初期値を使うという方はどのようなユースケースがあるかまだよく分かっていませんがとりあえず今回は true を指定すべきだろうということで以下の様に対応しました。

解決策

Atom 定義を以下の様にしました。

const initialState = {
  id: undefined
};

export const targetAtom = atomWithStorage<Target>(
  "target",
  initialState,
  undefined,
  { getOnInit: true }
);

上記の通り getOnInit: true に設定すると正しく初期化時にも保存された id の値が取得できました。

最後に

今回は簡単ですが、この仕様に気づかないとハマりかねない部分だったので記事にしておきました。他にも Store 系のライブラリは見ましたが Jotai の使い勝手はかなり良いと思うので今後も使っていきたいです。

参考書籍