JotaiJotai

状態
Primitive and flexible state management for React

atomWithStorage

引用: https://github.com/pmndrs/jotai/pull/394

import { useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
const darkModeAtom = atomWithStorage("darkMode", false);
const Page = () => {
const [darkMode, setDarkMode] = useAtom(darkModeAtom);
return (
<>
<h1>Welcome to {darkMode ? "dark" : "light"} mode!</h1>
<button onClick={() => setDarkMode(!darkMode)}>toggle theme</button>
</>
);
};

atomWithStorage 函数创建一个原子,其值保存在用于 React 的 localStoragesessionStorage 或用于 React Native 的 AsyncStorage 中。

参数

key (required): 与 localStorage、sessionStorage 或 AsyncStorage 同步状态时用作 key 的唯一字符串

initialValue (required): 原子初始值

storage (optional): 一个对象:

  • 用于存储/检索持久状态的 getItemsetItem 方法; 默认使用 localStorage 进行存储/检索,使用 JSON.stringify()/JSON.parse() 进行序列化/反序列化。
  • 可选地,存储有一个 subscribe 属性,可用于同步存储。 默认的 localStorage 处理跨表同步的 storage 事件。
  • 可选地,存储有一个 delayInit (boolean) 属性,可以用来告诉 jotai 是否使用 Suspense
    • delayInit: true 不会在第一次读取原子值时暂停
    • delayInit: false(默认)将在第一次读取原子值时暂停

服务器端渲染

任何依赖于存储原子值的 JSX 标记(例如,classNamestyle 属性)在服务器上呈现时将使用 initialValue(因为 localStoragesessionStorage 在服务器上不可用)。

这意味着如果用户的 storedValueinitialValue 不同,那么最初作为 HTML 提供给用户浏览器的内容与 React 在 rehydration 过程中所期望的内容之间将存在不匹配。

针对此问题的建议解决方法是通过将内容包装在 [自定义<ClientOnly>包装器](https://www.joshwcomeau.com/react/the-perils-of-rehydration/#abstractions),仅在 rehydration 后呈现。 替代解决方案在技术上是可行的,但在将 initialValue 交换为 storedValue 时需要短暂的“闪烁”,这可能会导致不愉快的用户体验,因此建议采用此解决方案。

从存储中删除项目

对于要从存储中删除项目的情况, 使用 atomWithStorage 创建的原子在写入时接受 RESET 符号。

有关用法,请参见以下示例:

import { useAtom } from "jotai";
import { atomWithStorage, RESET } from "jotai/utils";
const textAtom = atomWithStorage("text", "hello");
const TextBox = () => {
const [text, setText] = useAtom(textAtom);
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => setText(RESET)}>Reset (to 'hello')</button>
</>
);
};

如果需要,您还可以根据先前的值进行条件重置。

如果您希望在先前的值满足条件的情况下清除 localStorage 中的键,这将特别有用。

下面举例说明了这种用法,只要前一个值为 true,就会清除 visible 键。

import { useAtom } from 'jotai'
import { atomWithStorage, RESET } from 'jotai/utils'
const isVisibleAtom = atomWithStorage('visible', false)
const TextBox = () => {
const [isVisible, setIsVisible] = useAtom(isVisibleAtom)
return (
<>
{ isVisible && <h1>Header is visible!</h1> }
<button onClick={() => setIsVisible((prev) => prev ? RESET : true))}>Toggle visible</button>
</>
)
}

React-Native 实现

您可以使用任何实现 getItemsetItemremoveItem 的库。 假设您将使用社区提供的标准 AsyncStorage。

import { atomWithStorage, createJSONStorage } from "jotai/utils";
import AsyncStorage from "@react-native-async-storage/async-storage";
const storage = createJSONStorage(() => AsyncStorage);
const content = {}; // 任何可序列化的 JSON
const storedAtom = atomWithStorage("stored-key", content, storage);