JotaiJotai

ηŠΆζ…‹
Primitive and flexible state management for React

Async

All atoms support async behavior such as async read or async write. However there are APIs for more control described here.

unwrap

The unwrap util converts an async atom to a synchronous atom, making it easier to derive other atoms from its resolved value. While the promise is pending, it returns undefined or a configurable fallback, which can use the previous resolved value. Errors are not caught and are thrown when the atom is read.

This is useful because get in an atom's read function returns promises as-is rather than resolving them.

Signature

function unwrap<Value, Args extends unknown[], Result>(
anAtom: WritableAtom<Value, Args, Result>,
): WritableAtom<Awaited<Value> | undefined, Args, Result>
function unwrap<Value, Args extends unknown[], Result, PendingValue>(
anAtom: WritableAtom<Value, Args, Result>,
fallback: (prev?: Awaited<Value>) => PendingValue,
): WritableAtom<Awaited<Value> | PendingValue, Args, Result>
function unwrap<Value>(anAtom: Atom<Value>): Atom<Awaited<Value> | undefined>
function unwrap<Value, PendingValue>(
anAtom: Atom<Value>,
fallback: (prev?: Awaited<Value>) => PendingValue,
): Atom<Awaited<Value> | PendingValue>

Usage

import { atom } from 'jotai'
import { unwrap } from 'jotai/utils'
const countAtom = atom(0)
const delayedCountAtom = atom(async (get) => {
await new Promise((r) => setTimeout(r, 500))
return get(countAtom)
})
const unwrapped1Atom = unwrap(delayedCountAtom)
// The value is `undefined` while pending
const unwrapped2Atom = unwrap(delayedCountAtom, (prev) => prev ?? 0)
// The value is `0` initially, and subsequent updates keep the previous value.

atomWithObservable

Ref: https://github.com/pmndrs/jotai/pull/341

Usage

import { useAtom } from 'jotai'
import { atomWithObservable } from 'jotai/utils'
import { interval } from 'rxjs'
import { map } from 'rxjs/operators'
const counterSubject = interval(1000).pipe(map((i) => `#${i}`))
const counterAtom = atomWithObservable(() => counterSubject)
const Counter = () => {
const [counter] = useAtom(counterAtom)
return <div>count: {counter}</div>
}

The atomWithObservable function creates an atom from a rxjs (or similar) subject or observable. Its value will be last value emitted from the stream.

To use this atom, you need to wrap your component with <Suspense>. Check out guides/async.

Initial value

atomWithObservable takes second optional parameter { initialValue } that allows to specify initial value for the atom. If initialValue is provided then atomWithObservable will not suspend and will show initial value before receiving first value from observable. initialValue can be either a value or a function that returns a value

const counterAtom = atomWithObservable(() => counterSubject, {
initialValue: 10,
})
const counterAtom2 = atomWithObservable(() => counterSubject, {
initialValue: () => Math.random(),
})

Stackblitz

  • atomWithRefresh: re-evaluates an atom's read function to refresh data or retry a request.
  • useAtomValueRaw: reads an atom's value without resolving promises or suspending.
  • useAtomValueRawSync: the same as useAtomValueRaw, but uses useSyncExternalStore to avoid tearing and catch updates during mount.

loadable

The loadable util has been removed from jotai/utils in Jotai v3.

Use unwrap to derive a synchronous atom from an async atom with a configurable fallback while loading. Errors are still thrown. For reading an async atom's value in a component without suspending, see useAtomValueRaw and useAtomValueRawSync.

If you need to preserve the previous loading, hasData, and hasError states, see the v3 migration guide for a userland replacement built on unwrap.