Jotai: 速查指南
常用的 jotai 用法
安装
pnpm add jotaicheatsheet
| 用法 | 代码 |
|---|---|
| 定义 Atom | |
| rw: 使用 set/get Atom | |
| read: get Atom | |
write: set Atom(null) | |
| useHydrateAtoms SSR 防止状态不一致的闪烁问题 | |
| 使用 localStorage | |
常规使用 atom,可以多模块复用,链接: https://tutorial.jotai.org/quick-start/intro
import { atom, useAtom } from 'jotai';
const counter = atom(0);
export default function Page() {
const [count, setCounter] = useAtom(counter);
const onClick = () => setCounter(prev => prev + 1);
return (
<div>
<h1>{count}</h1>
<button onClick={onClick}>Click</button>
</div>
)
}使用 localStorage
import { useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
const theme = atomWithStorage('dark', false);
export default function Page() {
const [appTheme, setAppTheme] = useAtom(theme);
const handleClick = () => setAppTheme(!appTheme);
return (
<div className={appTheme? 'dark': 'light'}>
<h1>This is a theme switcher</h1>
<button onClick={handleClick}>{appTheme? 'DARK': 'LIGHT'}</button>
</div>
)
}read: useAtomValue: 只读
write: useSetAtom: 只写
链接: https://chat.qwen.ai/c/28ab834e-c680-4247-ae3a-447ea8a9b6de
const countAtom = atom(0)
const Counter = () => {
const setCount = useSetAtom(countAtom)
const count = useAtomValue(countAtom)
return (
<>
<div>count: {count}</div>
<button onClick={() => setCount(count + 1)}>+1</button>
</>
)
}Write Only
不是特别理解这种用法…
const switchAtom = atom(false)
const SetTrueButton = () => {
const setCount = useSetAtom(switchAtom)
const setTrue = () => setCount(true)
return (
<div>
<button onClick={setTrue}>Set True</button>
</div>
)
}
const SetFalseButton = () => {
const setCount = useSetAtom(switchAtom)
const setFalse = () => setCount(false)
return (
<div>
<button onClick={setFalse}>Set False</button>
</div>
)
}
export default function App() {
const state = useAtomValue(switchAtom)
return (
<div>
State: <b>{state.toString()}</b>
<SetTrueButton />
<SetFalseButton />
</div>
)
}读/写分离的意义
- 读写职责分离(Separation of Concerns)
- 读 atom(read-only):只提供
get函数,用于派生状态。 - 写 atom(write-only):只提供
set函数,常用于触发副作用或命令。 - 读写 atom(read-write):同时提供
get和set。
- 读 atom(read-only):只提供
这种分离让开发者能更清晰地表达意图:
- 如果一个 atom 只用于计算派生值(如
fullName = firstName + lastName),就不应该暴露set。 - 如果一个 atom 仅用于触发动作(如日志记录、导航),就不需要
get。这增强了代码的可读性和可维护性。
useAtom/useAtomValue
React 生态推崇 “最小权限原则”:
- 如果组件不需要修改状态,就不应获得修改能力。
- 类似于
useContext+useReducer中,将dispatch单独传递给需要的组件。
const fullNameAtom = atom((get) => get(firstNameAtom) + ' ' + get(lastNameAtom));
✅ useAtomValue(fullNameAtom):✅ 类型安全,正常工作。
❌ useAtom(fullNameAtom):
TypeScript 会报错(因为该 atom 不可写),或运行时报错(取决于 Jotai 版本)。
所以 useAtomValue 是消费只读 atom 的唯一正确方式。useHydrateAtoms
import { atom, useAtom } from 'jotai'
import { useHydrateAtoms } from 'jotai/utils'
const countAtom = atom(0)
const CounterPage = ({ countFromServer }) => {
useHydrateAtoms([[countAtom, countFromServer]])
const [count] = useAtom(countAtom)
// count would be the value of `countFromServer`, not 0.
}