跳转到内容

Persistence

Jotai 在 utils 包中提供了 atomWithStorage 函数,支持将状态持久化到 sessionStoragelocalStorageAsyncStorage 或 URL hash 中。

(注意:本指南略有过时,需要部分重写。)

以下是几种替代实现方式:

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
}

请务必查看异步文档以了解更多关于异步原子的使用细节。

与 AsyncStorage 类似,只需使用 atomWithStorage 工具函数,并将默认存储覆盖为 sessionStorage

import { atomWithStorage, createJSONStorage } from 'jotai/utils'
const storage = createJSONStorage(() => sessionStorage)
const someAtom = atomWithStorage('some-key', someInitialValue, storage)
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>
)
}

在 StackBlitz 中打开

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>
)
}

在 StackBlitz 中打开