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 <Provider>
at least one <Suspense>
is placed inside the <Provider>
.
const App = () => (<Provider><Suspense fallback="Loading..."><Layout /></Suspense></Provider>)
Having more <Suspense>
s in the component tree is possible.
Async read atom
The read
function of an atom can return a promise.
It will suspend and re-render when the promise is fulfilled.
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 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 is 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
There are another kind of async atoms, called async write atom.
When write
function of atom returns a promise, it may suspend.
This happens only if the atom is used directly with useAtom,
regardless of its value. (The atom value can be just 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 different with Suspense
.
A write atom will trigger the Suspense
fallback only if:
* the atom's write arg (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 properly set somePrimitiveAtom
to the correct values.