Pmndrs.docs

Async

This doc describes about the behavior with async.

Async support is first class in jotai. It fully leverages React Suppense.

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

Triggering 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&#39;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.