Async
Async support is first class in jotai. It fully leverages React Suspense.
Technically, Suspense usage other than React.lazy is still unsupported / undocumented in React 17. If this is blocking, check out guides/no-suspense.
Suspense
To use async atoms, you need to wrap your component tree with <Suspense>
.
If you have a
<Provider>
, place at least one<Suspense>
inside said<Provider>
; otherwise, it may cause an endless loop while rendering the components.
const App = () => (<Provider><Suspense fallback="Loading..."><Layout /></Suspense></Provider>)
Having more <Suspense>
s in the component tree is also possible.
Async read atom
The read
function of an atom can return a promise.
It will suspend and re-render once the promise fulfills.
Most importantly, useAtom only returns a resolved value.
const countAtom = atom(1)const asyncCountAtom = atom(async (get) => get(countAtom) * 2)// even though the read function returns a promise,const Component = () => {const [num] = useAtom(asyncCountAtom)// `num` is guaranteed to be a number.}
An atom becomes async not only if the atom read function is async, but also if one or more of its dependencies are async.
const anotherAtom = atom((get) => get(asyncCountAtom) / 2)// even though this atom doesn't return a promise,// it's a read async atom because `asyncCountAtom` is async.
Async write atom behavior until v1.3.9
(This is no longer the case since v1.4.0.)
Async write atom
Async write atoms are another kind of async atom.
When the write
function of atom returns a promise, it may suspend.
This happens if the atom is used directly with useAtom,
regardless of its value. (The atom value can be null
.)
const countAtom = atom(1)const asyncIncrementAtom = atom(null, async (get, set) => {// await somethingset(countAtom, get(countAtom) + 1)})const Component = () => {const [, increment] = useAtom(asyncIncrementAtom)// it will suspend while `increment` is pending.}
There's no way to know as of now if an atom suspends because of
read
orwrite
.
Triggering Suspense
fallback of write atom
This section applies only for "async write atom" not "async read atom", which works differently with Suspense
.
A write atom will trigger the Suspense
fallback if:
* the atom's write argument (2nd one) is async* the awaited call is made directly, and not from inside another containing function
This will trigger the Suspense
fallback:
const writeAtom = atom(null, async (get, set) => {const response = await new Promise<string>((resolve, _reject) => {setTimeout(() => {resolve('some returned value')}, 2000)})set(somePrimitiveAtom, 'The returned value is: ' + response)})
This will not trigger the Suspense
fallback:
const writeAtom = atom(null, (get, set) => {const getResponse = async () => {const response = await new Promise<string>((resolve, _reject) => {setTimeout(() => {resolve('some returned value')}, 2000)})set(somePrimitiveAtom, 'The returned value is: ' + response)}getResponse()})
But both of the above will still set somePrimitiveAtom
to the correct values.