Skip to content

Latest commit

History

History
123 lines (92 loc) 路 3.39 KB

async.mdx

File metadata and controls

123 lines (92 loc) 路 3.39 KB
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.

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