Vite+React+TypeScript+EsLintに Reciolを導入して Stateをローカルストレージで永続化する
今回はVite+React+TypeScript+EsLintにRecoilを導入して、RecoilのStateをローカルストレージで永続化してみるまでの手順です。
Recoil導入
特に難しいことはなく、Recoil公式の通りです。
Recolインストール
Recoilインストールです。
npm install recoil
eslintrc.ymlの設定
「eslint-plugin-react-hooks」プラグインを導入している場合は、Recoil用の設定を追加することが推奨されています。
「eslint-plugin-react-hooks」プラグインはEsLintの初期化コマンド「npx eslint --init」でインストールされています。
設定がまだであれば追加しておいた方がよいでしょう。
例えば、「eslint-plugin-react-hooks」の設定を以下のようにしている場合、
plugins:
- react
+ - react-hooks
- '@typescript-eslint'
- jest
- jest-dom
- testing-library
rules:
# Reactのインポートをチェックしない
react/react-in-jsx-scope: off
# セミコロンつけない
semi:
- error
- never
# デフォルトエクスポートをエラーにする
import/prefer-default-export: off
import/no-default-export: error
+ # フックのルールに準拠するようにコードを強制する
+ react-hooks/rules-of-hooks: error
+ # エフェクトの依存配列のチェック
+ react-hooks/exhaustive-deps: warn
RecoilのuseRecoilCallbackの設定を追加することが推奨されています。
useRecoilCallback() に渡された依存関係が正しく指定されていない場合は警告されます。
rules:
# Reactのインポートをチェックしない
react/react-in-jsx-scope: off
# セミコロンつけない
semi:
- error
- never
# デフォルトエクスポートをエラーにする
import/prefer-default-export: off
import/no-default-export: error
# フックのルールに準拠するようにコードを強制する
react-hooks/rules-of-hooks: error
# エフェクトの依存配列のチェック
- react-hooks/exhaustive-deps: warn
+ react-hooks/exhaustive-deps:
+ - warn
+ - additionalHooks: '(useRecoilCallback|useRecoilTransaction_UNSTABLE)'
動作確認用のプログラム
動作確認用に簡単なプログラムを作成します。
テキストボックスに入力した値を、ボタンクリックで登録します。
Recoilを使用するコンポーネントは、親ツリーのどこかに<RecoilRoot>
を配置する必要があります。ここではAppコンポーネントに配置しています。
import { RecoilRoot } from 'recoil'
import { AtomSample } from './AtomSample'
export function App() {
return (
<RecoilRoot>
<AtomSample />
</RecoilRoot>
)
}
「登録した値」を管理するためのAtomを作成します。
import { atom } from 'recoil'
export const addedValue = atom({
key: 'addedValue',
default: '',
})
登録ボタンのクリックで、先ほど作成した「登録した値」Atom にテキストボックスに入力した値をセットします。
import React from 'react'
import { useRecoilState } from 'recoil'
import * as atoms from './atoms'
export function AtomSample() {
const [inputValue, setInputValue] = React.useState('')
const [addedValue, setAddedValue] = useRecoilState(atoms.addedValue)
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value)
}
const onClick =() => {
setAddedValue(inputValue)
setInputValue('')
}
return (
<div>
<input type="text" value={inputValue} onChange={onChange} />
<button type="button" onClick={onClick}>登録</button>
<p>登録値:{addedValue}</p>
</div>
)
}
Atomの値をローカルストレージに保存する
先ほど作成したプログラムですが、リロードすると登録したAtomの値が消えてしまいます。
そこで、Atomの値をローカルストレージに保存します。
方法はいくつかあるようです。
- 公式サイトに記載の AtomEffect を使用する方法
https://recoiljs.org/docs/guides/atom-effects#local-storage-persistence - polemius/recoil-persist プラグインを使う方法
https://github.com/polemius/recoil-persist
今回は公式サイトの AtomEffect を使用する方法で、実装します。
コチラのサイトにtypescript用がありましたので、そのままいただきました。
参考:https://github.com/facebookexperimental/Recoil/issues/6
import { atom, AtomEffect, DefaultValue } from 'recoil'
+ const localStorageEffect: <T>(key: string) => AtomEffect<T> =
+ (key: string) =>
+ ({ setSelf, onSet }) => {
+ const savedValue = localStorage.getItem(key)
+
+ if (savedValue != null) {
+ setSelf(JSON.parse(savedValue))
+ }
+
+ onSet((newValue) => {
+ if (newValue instanceof DefaultValue) {
+ localStorage.removeItem(key)
+ } else {
+ localStorage.setItem(key, JSON.stringify(newValue))
+ }
+ })
+ }
export const addedValue = atom({
key: 'addvalue',
default: '',
+ effects_UNSTABLE: [
+ localStorageEffect<string>('addedValue')
+ ]
})
Atom Effects は Atom に値がセットされたときに、別の処理を実行させることができます。
setSelf には Atom の値を設定またはリセットするためのコールバック関数を指定します。
onSet は Atom の値が変化したときのコールバック関数を指定します。
リロードしても値が保持されるようになりました。
AtomEffect には、他にも
Atom の値がセットされたら、ログを記録したり、変更履歴を記録したりもできるようです。
Discussion