异步
使用异步原子,你可以在直接从你的原子管理它们的同时,轻松地访问真实世界的数据。
我们可以将它们分为两个主要类别:
- 异步读取原子:异步请求在你尝试获取其值的瞬间立即开始。你可以将它们视为 "智能获取器"。
- 异步写入原子:异步请求在特定时刻开始。你可以将它们视为 "动作"。
异步读取原子
原子的 read
函数可以返回一个 promise。
const countAtom = atom(1)const asyncAtom = atom(async (get) => get(countAtom) * 2)
Jotai 本质上利用 Suspense
来处理异步流。
const ComponentUsingAsyncAtoms = () => {const [num] = useAtom(asyncAtom)// 这里 `num` 总是 `number`,即使 asyncAtom 返回一个 Promise}const App = () => {return (<Suspense fallback={/* 挂起时显示的内容 */}><ComponentUsingAsyncAtoms /></Suspense>)}
另外,你可以通过使用 loadable
API 包装你的原子,来避免 Jotai 为你做的内在挂起。
如果另一个原子使用了异步原子,它将返回一个 promise。所以,我们需要使原子也变为异步。
const anotherAtom = atom(async (get) => (await get(asyncAtom)) / 2)
这也适用于带有写入函数的原子。
const asyncAtom = atom(async (get) => ...)const writeAtom = atom(null, async (get, set, payload) => {await get(asyncAtom)// ...})
异步写入原子
异步写入原子是另一种异步原子。当原子的 write
函数返回一个 promise 时。
const countAtom = atom(1)const asyncIncrementAtom = atom(null, async (get, set) => {// 等待某事set(countAtom, get(countAtom) + 1)})const Component = () => {const [, increment] = useAtom(asyncIncrementAtom)const handleClick = () => {increment()}// ...}
有时异步
一个可以用 Jotai 实现的有趣模式是在需要触发挂起时从异步切换到同步。
const request = async () => fetch('https://...').then((res) => res.json())const baseAtom = atom(0)const Component = () => {const [value, setValue] = useAtom(baseAtom)const handleClick = () => {setValue(request()) // 将挂起,直到请求解析}// ...}
在 TypeScript 中的使用
在 TypeScript 中 atom(0)
被推断为 PrimitiveAtom<number>
。它不能接受 Promise<number>
作为值,所以前面的代码将无法进行类型检查。为了适应这个,你需要显式地为你的原子进行类型注解,并添加 Promise<number>
作为接受的值。
const baseAtom = atom<number | Promise<number>>(0) // 将接受同步和异步值
永远异步
有时你可能希望挂起,直到一个不确定的时刻(或永不)。
const baseAtom = atom(new Promise(() => {})) // 将挂起,直到设置为其他值
Suspense
在 Jotai 中,异步支持是一流的。它在核心上完全利用了 React Suspense。
技术上,除了 React.lazy 之外的 Suspense 使用在 React 17 中仍然不受支持/未记录。如果这是阻碍,那么你仍然可以使用
loadable
API 来避免挂起
要使用异步原子,你需要用 <Suspense>
包裹你的组件树。
如果你有一个
<Provider>
,请在该<Provider>
内放置至少一个<Suspense>
;否则,它可能会在渲染组件时造成无尽的循环。
const App = () => (<Provider><Suspense fallback="加载中..."><Layout /></Suspense></Provider>)
在组件树中有更多的 <Suspense>
也是可能的,并且必须考虑以最大程度地利用 Jotai 的内在处理。