title | description | nav |
---|---|---|
Async |
This doc describes about the behavior with async. |
1.03 |
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.
To use async atoms, you need to wrap your component tree with <Suspense>
.
If you have <Provider>
, place at least one <Suspense>
inside the <Provider>
.
const App = () => (
<Provider>
<Suspense fallback="Loading...">
<Layout />
</Suspense>
</Provider>
)
Having more <Suspense>
s in the component tree is also possible.
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 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 something
set(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
.
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.