概要
atomWithStorageでMap型を扱う際に躓いたため実装方法をメモついでに記事にしました。
下記の実装ではローカルストレージへの書き込みやローカルストレージからのロードでエラーが出たり、うまく動作しませんでした。
const sampleAtom = atomWithStorage<SampleMap>(
'sample_map',
new Map(),
)
ローカルストレージはstringのみ扱えるためMap型の値をJSON形式の文字列としてローカルストレージに書き込み、ローカルストレージからのロードする際は文字列型からJSONに変換し、それをMap型に変換する必要がある。
正しい実装方法
atomWithStorageのstorageパラメータにカスタム関数を渡すことで解決する。
storage (optional): an object with the following methods:
getItem(key, initialValue) (required): Reads an item from storage, or falls back to ?the intialValue
setItem(key, value) (required): Saves an item to storage
removeItem(key) (required): Deletes the item from storage
subscribe(key, callback, initialValue) (optional): A method which subscribes to >external storage updates.
type SampleMapItem = {
value1: string
value2: number
}
type SampleMap = Map<string, SampleMapItem>
// Mapのシリアライズ・デシリアライズ
const serializeMap = (map: Map<string, SampleMapItem>) =>
JSON.stringify(Array.from(map.entries()))
const deserializeMap = (str: string) => new Map(JSON.parse(str))
// localStorageにMapを保存するための関数
const localStorageWithMap = {
getItem: (key: string, initialValue: SampleMap) => {
const item = localStorage.getItem(key)
return item ? deserializeMap(item) : new Map()
},
setItem: (key: string, newValue: SampleMap) => {
localStorage.setItem(key, serializeMap(newValue))
},
removeItem: (key: string) => {
localStorage.removeItem(key)
},
}
// atomの定義
const sampleAtom = atomWithStorage<SampleMap>(
'sample_map',
new Map(),
localStorageWithMap
)
最後に
jotaiはあまり詳しくないですが、おそらくこれがベストプラクティスだと思っています。
もし他の良い方法があれば教えていただければ幸いです。