question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Question] Can jotai be used without Suspense?

See original GitHub issue

In @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) image

  • Async actions (async write) image


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:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:13 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
Pinpicklecommented, Aug 22, 2021

What do you think about also having a useLoadableAtomValue hook that automatically creates a

We don’t do that. We focus on utils on atom level, not hook level. (We prefer minimal/primitive API.)

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 the SUSPENDED symbol, which makes it just a little bit simpler to deal with.

I’ll make a new issue so we can discuss this

1reaction
dai-shicommented, Aug 22, 2021

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

import { loadable } from 'jotai/utils'

const anAtom = atom(aync (get) => { ... })
const anAtomThatDoesntThrowPromise = loadable(anAtom) // { data, loading, error }

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?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Jotai: The Ultimate React State Management - 100ms
Jotai is a relatively new state management library for React. It's simple, but make no mistakes, it's a robust library. Jotai is based...
Read more >
Jotai: Atom-based state management for React
Jotai is a simple state management library by the same creators of Zustand. ... Therefore, you can use it even from outside of...
Read more >
Jotai vs. Recoil: What are the differences? - LogRocket Blog
Jotai and Recoil both have their benefits. This article compares the state management libraries to help you decide what's best for you.
Read more >
Jotai - State Simply Managed - Paolo Tiu
Jotai has first-class support for async. It fully leverages React Suspense. They have fantastic docs. Check it out! Conclusion. Jotai is my ...
Read more >
NextJS - ReactDOMServer does not yet support Suspense
You can use React 18 features like suspense in Next.js Advanced Features. Obviously it's still experimental and might cause issues with you application....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found