🎾

【Next.js/Recoil】リロードでグローバルステートを消さないために

2024/01/12に公開

「その状態 消えるよ」


ページをまたいで保持したいステートの管理にRecoilを使用しています。が、ステートを保持しておきたいページがリロードされるとそのステートが破棄されてしまいます。理由は、リロード操作はブラウザのものでありReactの外で行われるから(雑)。そりゃそうだ…。

解決できたのですが、Next.jsとの組み合わせでハマるところがあったので、この記事ではその部分も含めてシェアします。

解決策:recoil-persistの使用

Recoilのステートを永続化するrecoil-persistというライブラリで解決できました。

https://github.com/polemius/recoil-persist

ローカルストレージやセッションストレージにRecoilの情報を読み書きすることで、先述のリロードによるステートの破棄を回避する実装のようです。

実装はシンプルで、既存のAtomにrecoil-persistの追加設定を加えるような感じ。

const SomeGlobalState = atom<string>({
  key: 'SomeGlobalState',
  default: '',
})

これに以下の記述を加えます。(今回はセッションストレージですが、デフォルトではローカルストレージです)

+ const { persistAtom } = recoilPersist({
+   key: 'some-persist-state',
+   storage: typeof window === 'undefined' ? undefined : window.sessionStorage,
+ })

const SomeGlobalState = atom<string>({
  key: 'SomeGlobalState',
  default: '',
+ effects_UNSTABLE: [persistAtom],
})

既存のRecoilの設定にちょい足しで実装できるので助かりますね。

これでセッションストレージに次のようにステートが保存されます。

キー
some-persist-state {"SomeGlobalState":"ここにステートの値"}

デフォルトでは上記のようにJSONライクになっていますが、自分でカスタマイズもできるようです。(Recoilのインターフェースを通して値を使うのでこの形式にこだわるケースは少なそう?)

ポイントはstorage: typeof window === 'undefined' ? undefined : window.sessionStorageの部分で、これはNext.jsのようにSSRを使用する(こともある)場合に有用なチェックです。
ここを単にstorage: window.sessionStorageとしてしまうと、window is not definedのエラーが出てしまいます。クライアントサイドでのみ設定するようにすると動きました。

別の方法は?

結局ローカルストレージやセッションストレージに保存しているだけなら、Recoilを使用する必要がないのでは…? という疑問。

そう思います。が、すでにRecoilで実装してあるところに追加でリロードに対応するような場合、今回のような小さな付け足しで実装可能なのでこれはこれでアリかと。

一方で、SWRなどのおかげでグローバルステートを管理する機会が減ったり、Recoilのメンテナンス状況が芳しくない問題もあるので、今後イチから実装する場合は直接ストレージにアクセスするロジックを書く方針も要検討だなーと思いました。

Discussion