JotaiJotai

状態
Primitive and flexible state management for React

No Suspense

The content of this page will be easier to follow if you have taken a look at the basics/async part of the docs.

Sometimes we need to wrap our components in suspense to use asynchronous atoms. Two kinds of atoms need Suspense.

  • Derived async atoms (async read function)
  • Asynchronous actions (async write)

But sometimes we're not allowed to wrap our components in Suspense, So we're going to do some hacks below and and make our async atoms synchronous.

Async read

Check this derived async atom:

const urlAtom = atom('https://json.host.com')
const fetchUrlAtom = atom(async (get) => {
const response = await fetch(get(urlAtom))
return await response.json()
})

It needs Suspense because the read function returns a promise(async function).

We shouldn't return a promise if we don't need it to suspend, so we have to change the structure slightly.

Here's the new code.

const fetchResultAtom = atom({ loading: true, error: null, data: null })
const runFetchAtom = atom(
(get) => get(fetchResultAtom),
(_get, set, url) => {
const fetchData = async () => {
set(fetchResultAtom, (prev) => ({ ...prev, loading: true }))
try {
const response = await fetch(url)
const data = await response.json()
set(fetchResultAtom, { loading: false, error: null, data })
} catch (error) {
set(fetchResultAtom, { loading: false, error, data: null })
}
}
fetchData()
}
)
runFetchAtom.onMount = (runFetch) => {
runFetch('https://json.host.com')
}
const Component = () => {
const [result] = useAtom(runFetchAtom)
console.log(result) // { loading: ..., error: ..., data: ... }
return <div>...</div>
}

Async write

The original code is:

const fetchCountAtom = atom(
(get) => get(countAtom),
async (_get, set, url) => {
const response = await fetch(url)
set(countAtom, (await response.json()).count)
}
)

The write function (2nd arg) returns a promise, which will suspend.

We just need to make it synchronous and returns nothing, just like this:

const fetchCountAtom = atom(
(get) => get(countAtom),
(_get, set, url) => {
const fetchData = async () => {
const response = await fetch(url)
set(countAtom, (await response.json()).count)
}
fetchData()
}
)