[Question] Can jotai be used without Suspense?
See original GitHub issueIn @JLarky 's article, it notes:
The first set of features that stands out are functions that help to work with async selectors. For example, if you want to work with an async atom in a context where Suspense would not be appropriate, you can always consume it with useRecoilStateLoadable or useRecoilValueLoadable so it will no longer throw errors or promises.
The question is if we can use jotai without Suspense. Actually, we can under some constraints.
There are two parts it requires Suspense.
-
Derived async atoms (async
read
) -
Async actions (async
write
)
The latter is easier.
The original code is this:
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.
So, We just don’t return anything. The modified code is 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()
}
)
Okay, the difficult one is async read. The original code is this:
const urlAtom = atom("https://json.host.com")
const fetchUrlAtom = atom(
async get => {
const response = await fetch(get(urlAtom))
return await response.json()
}
)
We can’t make read
function to return a promise.
So, we have to change the structure.
Here’s the new code:
const fetchResultAtom = atom({ loading: true, error: null, data: null })
const runFetchAtom = atom(null, (_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()
})
const Component = () => {
const [, runFetch] = useAtom(runFetchAtom)
useEffect(() => {
runFetch("https://json.host.com")
}, [])
Yes, we need useEffect for initialization. This would be mitigated once atom effects #211 lands.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:13 (10 by maintainers)
Oops, this sentence wasn’t supposed to make it in, I thought I edited it out. I completely agree.
The nice thing about the
suspendable
API is that it’s just a simple type union with theSUSPENDED
symbol, which makes it just a little bit simpler to deal with.I’ll make a new issue so we can discuss this
@Pinpickle Wow, you resolved it! https://codesandbox.io/s/stoic-feistel-qo849?file=/src/App.js Yes, this looks like a good solution.
I think this is a good workaround. We can add it to https://docs.pmnd.rs/jotai/guides/no-suspense.
We could even consider adding a new util.
Maybe it should handle error too, instead of throwing to ErrorBoundary. The implementation would be somewhat close to
waitFor
. (maybe not so close, probably simpler, and this time it’s writable.)How does that sound? Would you be interested in working on it?