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
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.