Persistence
Jotai 在 utils 包中提供了 atomWithStorage 函数,支持将状态持久化到 sessionStorage、localStorage、AsyncStorage 或 URL hash 中。
(注意:本指南略有过时,需要部分重写。)
以下是几种替代实现方式:
使用 localStorage 的简单模式
Section titled “使用 localStorage 的简单模式”const strAtom = atom(localStorage.getItem('myKey') ?? 'foo')
const strAtomWithPersistence = atom( (get) => get(strAtom), (get, set, newStr) => { set(strAtom, newStr) localStorage.setItem('myKey', newStr) },)使用 localStorage 和 JSON 解析的辅助函数
Section titled “使用 localStorage 和 JSON 解析的辅助函数”const atomWithLocalStorage = (key, initialValue) => { const getInitialValue = () => { const item = localStorage.getItem(key) if (item !== null) { return JSON.parse(item) } return initialValue } const baseAtom = atom(getInitialValue()) const derivedAtom = atom( (get) => get(baseAtom), (get, set, update) => { const nextValue = typeof update === 'function' ? update(get(baseAtom)) : update set(baseAtom, nextValue) localStorage.setItem(key, JSON.stringify(nextValue)) }, ) return derivedAtom}(应添加错误处理。)
使用 AsyncStorage 和 JSON 解析的辅助函数
Section titled “使用 AsyncStorage 和 JSON 解析的辅助函数”此方式需要 onMount。
const atomWithAsyncStorage = (key, initialValue) => { const baseAtom = atom(initialValue) baseAtom.onMount = (setValue) => { ;(async () => { const item = await AsyncStorage.getItem(key) setValue(JSON.parse(item)) })() } const derivedAtom = atom( (get) => get(baseAtom), (get, set, update) => { const nextValue = typeof update === 'function' ? update(get(baseAtom)) : update set(baseAtom, nextValue) AsyncStorage.setItem(key, JSON.stringify(nextValue)) }, ) return derivedAtom}请务必查看异步文档以了解更多关于异步原子的使用细节。
sessionStorage 示例
Section titled “sessionStorage 示例”与 AsyncStorage 类似,只需使用 atomWithStorage 工具函数,并将默认存储覆盖为 sessionStorage。
import { atomWithStorage, createJSONStorage } from 'jotai/utils'
const storage = createJSONStorage(() => sessionStorage)const someAtom = atomWithStorage('some-key', someInitialValue, storage)序列化原子模式
Section titled “序列化原子模式”type Actions = | { type: 'serialize'; callback: (value: string) => void } | { type: 'deserialize'; value: string }
const serializeAtom = atom(null, (get, set, action: Actions) => { if (action.type === 'serialize') { const obj = { todos: get(todosAtom).map(get), } action.callback(JSON.stringify(obj)) } else if (action.type === 'deserialize') { const obj = JSON.parse(action.value) // needs error handling and type checking set( todosAtom, obj.todos.map((todo: Todo) => atom(todo)), ) }})
const Persist = () => { const [, dispatch] = useAtom(serializeAtom) const save = () => { dispatch({ type: 'serialize', callback: (value) => { localStorage.setItem('serializedTodos', value) }, }) } const load = () => { const value = localStorage.getItem('serializedTodos') if (value) { dispatch({ type: 'deserialize', value }) } } return ( <div> <button onClick={save}>Save to localStorage</button> <button onClick={load}>Load from localStorage</button> </div> )}使用 atomFamily 的模式
Section titled “使用 atomFamily 的模式”type Actions = | { type: 'serialize'; callback: (value: string) => void } | { type: 'deserialize'; value: string }
const serializeAtom = atom(null, (get, set, action: Actions) => { if (action.type === 'serialize') { const todos = get(todosAtom) const todoMap: Record<string, { title: string; completed: boolean }> = {} todos.forEach((id) => { todoMap[id] = get(todoAtomFamily({ id })) }) const obj = { todos, todoMap, filter: get(filterAtom), } action.callback(JSON.stringify(obj)) } else if (action.type === 'deserialize') { const obj = JSON.parse(action.value) // needs error handling and type checking set(filterAtom, obj.filter) obj.todos.forEach((id: string) => { const todo = obj.todoMap[id] set(todoAtomFamily({ id, ...todo }), todo) }) set(todosAtom, obj.todos) }})
const Persist = () => { const [, dispatch] = useAtom(serializeAtom) const save = () => { dispatch({ type: 'serialize', callback: (value) => { localStorage.setItem('serializedTodos', value) }, }) } const load = () => { const value = localStorage.getItem('serializedTodos') if (value) { dispatch({ type: 'deserialize', value }) } } return ( <div> <button onClick={save}>Save to localStorage</button> <button onClick={load}>Load from localStorage</button> </div> )}