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.

`loadable` API for using async atoms without suspense

See original GitHub issue

Prior discussion: https://github.com/pmndrs/jotai/issues/269

Recoil has a useRecoilValueLoadable for bypassing suspense and returning a “loadable” object. This is useful in some cirumstances when suspense is not desired, and is also much easier than implementing it via the method recommended in the Jotai docs.

No-suspense code example
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>
}

I’m proposing a read-only loadable util which returns an atom that converts an async atom into a sync atom, just like Recoil’s loadable utils.

Depending on context, consumers may want errors to still be thrown or captured into the loadable object. There are two ways of handling this.

1. Separate suspendable/loadable APIs

const SUSPENDED = Symbol("jotai/atomSuspended");

type Loadable<T, TError = any> = { type: "loading" } | { type: "value", value: T } | { type: "error", value: TError };

// will still throw on error
const suspendable = <T>(atom<Promise<T> | T>) => T | SUSPENDED;

// will not throw on error
const loadable = <T>(atom<Promise<T> | T>) => Loadable<T>;

This has the advantage of calling a single function without parameters whether you want errors to be thrown or not. The suspendable type signature is also much simpler, as we don’t need to be able to disambiguate between value and error types.

2. Param in the loadable function

function loadable<Value, Update>(
  atom: WritableAtom<Value, Update>
): WritableAtom<{ loading: true; data?: Value } | { data: Value } | { error: unknown }, Update>

function loadable<Value, Update>(
  atom: WritableAtom<Value, Update>,
  throwsError: boolean
): WritableAtom<{ loading: true; data?: Value } | { data: Value }, Update>

function loadable<Value>(
  atom: Atom<Value>
): Atom<{ loading: true; data?: Value } | { data: Value } | { error: unknown }>

function loadable<Value, Update>(
  atom: Atom<Value, Update>,
  throwsError: boolean
): Atom<{ loading: true; data?: Value } | { data: Value }>

This has the advantage of there only being one function. However, a flag with a default value means one option will be more convenient than another.


Outstanding questions:

  • Which API to choose?
  • Should this support writable atoms in the first implementation? Or can that wait?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:29 (21 by maintainers)

github_iconTop GitHub Comments

5reactions
Pinpicklecommented, Dec 17, 2021

@dai-shi I haven’t forgotten this, just super busy at the moment. If this isn’t done by the time I get some time to do so, I certainly will!

3reactions
aulneaucommented, Aug 22, 2021

This makes me so happy to see! I think this will enable a lot more usage from folks who maybe were hesitant due to suspense only support.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Async — Jotai, primitive and flexible state management for ...
Async support is first class in Jotai. It fully leverages React Suspense at its core. ... To use async atoms, you need to...
Read more >
Asynchronous Data Queries - Recoil
Asynchronous Data Queries. Recoil provides a way to map state and derived state to React components via a data-flow graph. What's really powerful...
Read more >
Suspense for Data Fetching (Experimental) - React
Approach 1: Fetch-on-Render (not using Suspense) ... In this example, two components wait for an asynchronous API call to fetch some data:.
Read more >
Exploring Asynchronous Requests in Recoil - AppSignal Blog
The <Suspense /> component declaratively waits for data to load and defines a loading state. Recoil hooks into this React component when the ......
Read more >
Why We Need Jotai v2 API - Daishi Kato's blog
We can no longer read async atom values in sync, obviously. To use async atom values in sync, loadable util may help to...
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