Package @reatom/extensions
@reatom/extensions
Interfaces
AbortExt
Defined in: withAbort.ts:10
Extended by
Properties
abort
abort:
Action<[any]>
Defined in: withAbort.ts:11
DynamicSubscriptionExt
Defined in: withDynamicSubscription.ts:9
This interface improve .subscribe method behavior by relying it on
abortVar. It performs unsubscribe automatically, when abort will occur.
SuspenseRecord
Defined in: withSuspense.ts:10
Internal suspense cache record tracking promise state. Do not use it directly, only for libraries!
Properties
kind
kind:
"fulfilled"|"rejected"|"pending"
Defined in: withSuspense.ts:11
value
value:
any
Defined in: withSuspense.ts:12
Type Aliases
SuspenseExt
SuspenseExt<
State> =object
Defined in: withSuspense.ts:72
Extension type that adds a suspended computed atom to track resolved values
from async atoms.
Type Parameters
State
State
Properties
suspended
suspended:
Computed<Awaited<State>>
Defined in: withSuspense.ts:73
Variables
SUSPENSE
SUSPENSE:
WeakMap<Promise<any>,SuspenseRecord>
Defined in: withSuspense.ts:19
Internal suspense cache mapping promises to their settlement state. Do not use it directly, only for libraries!
withSuspenseInit()
withSuspenseInit: {<
State>():Ext<Atom<Promise<State>, [Promise<State>]>,Atom<State, [State]>>; <Target>(cb):Ext<Target>; }
Defined in: withSuspense.ts:261
Extension that enables asynchronous initialization for synchronous atoms. This feature bridges async data loading with sync atom semantics.
During initialization, if the result is a Promise, it throws the promise (suspense pattern) and schedules setting the atom’s state when resolved. After initialization completes, the atom operates fully synchronously.
This is perfect for local-first architectures: load data asynchronously on
init, then work with it synchronously. Combine with withChangeHook to sync
changes back to a server or database.
Without callback: Transforms Atom<Promise<State>> into Atom<State>.
The atom’s async initializer is unwrapped, and consumers receive the resolved
value.
Call Signature
<
State>():Ext<Atom<Promise<State>, [Promise<State>]>,Atom<State, [State]>>
Type Parameters
State
State
Returns
Ext<Atom<Promise<State>, [Promise<State>]>, Atom<State, [State]>>
Call Signature
<
Target>(cb):Ext<Target>
Type Parameters
Target
Target extends AtomLike<any, any[], any>
Parameters
cb
(state?) => AtomState<Target> | Promise<AtomState<Target>>
Returns
Ext<Target>
Examples
const userSettings = atom(async () => { const response = await fetch('/api/settings') return response.json() }).extend(withSuspenseInit()) // Type: Atom<Settings> (not Atom<Promise<Settings>>)
effect(() => { // After init completes, reads are synchronous const settings = userSettings() console.log(settings.theme) })// With callback: Provides an async initializer for any atom type, keeping the original state type. // Local-first pattern: async init + sync operations + sync-back const todos = atom<Todo[]>([]).extend( withSuspenseInit(async () => { const cached = await indexedDB.get('todos') return cached ?? [] }), withChangeHook((newState) => { // Sync changes back to storage indexedDB.set('todos', newState) }), )// Typed async init with custom default const profile = atom<{ username: string; age: number }>( throwAbort, ).extend( withSuspenseInit(async () => { const data = await fetchProfile() return data ?? { username: 'guest', age: 0 } }), )
@overload@overloadParam
Async or sync initializer function. Receives the current init state and returns the new state (or Promise of it).
Returns
Extension that unwraps Atom<Promise<State>> to Atom<State>
Returns
Extension that initializes the atom with the callback result
Functions
addCallHook()
addCallHook<
Target>(target,cb):Unsubscribe
Defined in: withChangeHook.ts:257
Dynamically adds a call hook to an existing action and returns a function to remove it.
Unlike withCallHook which is applied at action definition time,
addCallHook allows you to add and remove hooks at runtime. This is useful
for temporary subscriptions, conditional hook behavior, or when integrating
with external systems that need to be connected and disconnected
dynamically.
This feature is rarely needed, you should prefer using effect with
getCalls or take instead.
Type Parameters
Target
Target extends Action<any[], any>
The action type
Parameters
target
Target
The action to attach the hook to
cb
(payload, params) => void
Callback fired when the action is called
Returns
Unsubscribe function to remove this specific hook
See
withCallHook For adding hooks at action definition time
addChangeHook()
addChangeHook<
T>(target,cb):Unsubscribe
Defined in: withChangeHook.ts:118
Dynamically adds a change hook to an existing atom and returns a function to remove it.
Unlike withChangeHook which is applied at atom definition time,
addChangeHook allows you to add and remove hooks at runtime. This is useful
for temporary subscriptions or when you need conditional hook behavior that
can be enabled/disabled dynamically.
This feature is rarely needed, you should prefer using effect with
ifChanged or take instead.
Type Parameters
T
T extends AtomLike<any, any[], any>
The atom type
Parameters
target
T
The atom to attach the hook to
cb
(state, prevState?) => void
Callback fired when state changes
Returns
Unsubscribe function to remove this specific hook
See
withChangeHook For adding hooks at atom definition time
isInit()
isInit():
boolean
Defined in: withInit.ts:22
Checks if the current execution context is within the initialization of the current atom.
Returns
boolean
True if currently in the initialization phase, false otherwise
Example
const search = atom('', 'search').extend(withSearchParams('search')) const page = atom(1, 'page').extend( withSearchParams('page'), withComputed((state) => { search() // subscribe to the search changes // do NOT drop the persisted state on init return isInit() ? state : 1 }), )settled()
settled<
Result,Fallback>(promise,fallback?):Result|Fallback
Defined in: withSuspense.ts:39
Checks if a promise is settled and returns its value or fallback. If the promise is fulfilled, returns the resolved value. If the promise is rejected, throws the error. If the promise is pending, returns the fallback value (defaults to undefined).
Uses an internal WeakMap cache to track promise states across calls.
Type Parameters
Result
Result
Fallback
Fallback = undefined
Parameters
promise
The promise or synchronous value to check
Result | Promise<Result>
fallback?
Fallback
The value to return if the promise is still pending
Returns
Result | Fallback
The resolved value if fulfilled, throws if rejected, or fallback if pending
Example
const promise = Promise.resolve(42) await promise const value = settled(promise) // 42suspense()
suspense<
State>(target):Awaited<State>
Defined in: withSuspense.ts:194
Helper function to access the suspended value of an atom. Automatically
applies withSuspense() extension if the atom doesn’t already have it.
This function:
- Returns the resolved value if the promise is fulfilled
- Throws the promise if it’s still pending (for Suspense boundaries)
- Throws the error if the promise is rejected
Type Parameters
State
State
Parameters
target
AtomLike<State>
The atom to get the suspended value from
Returns
Awaited<State>
The resolved value (Awaited
Remarks
If withSuspense is already applied with different preserve options, the
behavior may be inconsistent. Consider applying withSuspense() explicitly
to control options.
Example
const data = computed(async () => { const response = await fetch('/api/data') return response.json() }, 'data')
// Automatically applies withSuspense() and returns suspended value const result = computed(() => { try { return suspense(data) // throws promise if pending } catch (promise) { if (promise instanceof Promise) { // Handle pending state return undefined } throw promise // Re-throw errors } }, 'result')withAbort()
withAbort(
strategy):AssignerExt<AbortExt>
Defined in: withAbort.ts:14
Parameters
strategy
"last-in-win" | "first-in-win"
Returns
withCallHook()
withCallHook<
Target>(cb):Ext<Target>
Defined in: withChangeHook.ts:204
Executes a callback whenever the target action is called.
This extension enables you to react to action invocations, making it invaluable for creating stable connections between independent features. The hook fires in the “Hooks” phase (after Updates, before Computations) and receives both the action’s return value and its parameters.
When to use:
- Creating stable cross-module connections that react to specific actions
- Tracking action calls for analytics, logging, or debugging
- Triggering side effects in response to action completions
- Coordinating behavior between independent features without coupling them
- Implementing event-driven communication patterns
When NOT to use:
- In dynamic features, like from computed factories (
takeoreffectandgetCallsinstead) - When you can achieve the same goal with direct action composition
For actions extended with withAsync, you can also hook into .onFulfill,
.onReject, or .onSettle to react to async completion events.
Type Parameters
Target
Target extends Action<any[], any>
The action type being extended
Parameters
cb
(payload, params) => void
Callback fired when action is called. Receives:
payload- The return value of the actionparams- The parameters passed to the action as an array
Returns
Ext<Target>
Extension function to be used with .extend()
Examples
// Cross-module coordination: Analytics tracking // In checkoutModule.ts export const submitOrder = action(async (order) => { const result = await api.submitOrder(order) return result }, 'submitOrder')
// In analyticsModule.ts import { submitOrder } from './checkoutModule' submitOrder.extend( withCallHook((promise, params) => { const [order] = params analytics.track('new_order', { orderId: order.id, total: order.total, }) }), )// Stable feature connection: Form submission tracking const fetch = action(async (param: number) => { const data = await api.fetch(param) return data }, 'fetch').extend(withAsync())
fetch.onFulfill.extend( withCallHook((call) => { console.log('Fetch completed', call.payload) }), )Throws
If callback is not a function
Throws
If applied to an atom instead of an action
See
- addCallHook For dynamically adding/removing call hooks
- withChangeHook For reacting to atom state changes instead
- withAsync For async action lifecycle hooks (onFulfill, onReject, onSettle)
withChangeHook()
withChangeHook<
Target>(cb):Ext<Target>
Defined in: withChangeHook.ts:70
Executes a callback whenever the target atom’s state changes.
This extension is essential for creating stable, declarative connections between independent modules or features. The hook fires in the “Hooks” phase of Reatom’s lifecycle (after Updates, before Computations), making it perfect for triggering side effects or synchronizing state across module boundaries.
When to use:
- Creating stable connections between features that shouldn’t depend on each other directly
- Triggering validation when a field’s value or state changes
- Syncing derived state in response to source state changes
- Managing side effects like DOM updates or analytics based on state changes
- Coordinating behavior across module boundaries without coupling them
When NOT to use:
- In dynamic features, like from computed factories (use
takeoreffectandifChangedinstead) - When a regular computed dependency would suffice
- For connection/disconnection events (use
withConnectHookinstead)
The callback receives the new state and previous state. It only fires when
the state actually changes (referential inequality check via Object.is).
The callback executes within the same reactive context, so you can safely
call other atoms and actions.
Type Parameters
Target
Target extends AtomLike<any, any[], any>
The atom type being extended
Parameters
cb
(state, prevState) => void
Callback fired when state changes. Receives:
state- The new state valueprevState- The previous state value (undefined on first change)
Returns
Ext<Target>
Extension function to be used with .extend()
Examples
// Basic usage: React to atom state changes const theme = reatomEnum(['light', 'dark', 'system']).extend( withChangeHook((state, prevState) => { document.body.classList.remove(prevState) document.body.classList.add(state) }), )// Stable feature connection: Analytics tracking // In userModule.ts export const userAtom = atom({ id: null, name: '' }, 'user')
// In analyticsModule.ts import { userAtom } from './userModule' userAtom.extend( withChangeHook((user, prevUser) => { if (user.id !== prevUser?.id) { analytics.identify(user.id, { name: user.name }) } }), )Throws
If callback is not a function
See
- addChangeHook For dynamically adding/removing change hooks
- withCallHook For reacting to action calls instead of state changes
- withConnectHook For reacting to connection lifecycle events
withComputed()
withComputed<
Target>(computed,options?):Ext<Target>
Defined in: withComputed.ts:25
A middleware extension that enhances an atom with computed capabilities.
Type Parameters
Target
Target extends AtomLike<any, any[], any>
The target atom or action type to be extended with computed functionality.
Parameters
computed
(state) => AtomState<Target>
A function that computes the new state based on the current state.
options?
Configuration options. Default is {}
tail?
boolean = true
Determines the order of the passed
computed calling. ATTENTION: use false only for computed with fixed size
of dependencies. Default is true
Returns
Ext<Target>
The extended atom or action with computed functionality.
withConnectHook()
withConnectHook<
Target>(cb):Ext<Target>
Defined in: withConnectHook.ts:10
Type Parameters
Target
Target extends AtomLike<any, any[], any>
Parameters
cb
(target) => void | Unsubscribe | Promise<void | Unsubscribe>
Returns
Ext<Target>
withDisconnectHook()
withDisconnectHook<
Target>(cb):Ext<Target>
Defined in: withConnectHook.ts:38
Type Parameters
Target
Target extends AtomLike<any, any[], any>
Parameters
cb
(target) => void
Returns
Ext<Target>
withDynamicSubscription()
withDynamicSubscription<
Target>(): (target) =>Target&DynamicSubscriptionExt
Defined in: withDynamicSubscription.ts:12
Type Parameters
Target
Target extends AtomLike<any, any[], any>
Returns
(
target):Target&DynamicSubscriptionExt
Parameters
target
Target
Returns
Target & DynamicSubscriptionExt
withInit()
withInit<
Target>(init):Ext<Target>
Defined in: withInit.ts:57
Define dynamically computed initial value for an atom.
Typically, you can use just an init callback in atom first argument:
atom(() => new Date()). But if you need to add initial callback after the
atom creation, so there this extensions is useful.
Type Parameters
Target
Target extends AtomLike<any, any[], any>
The atom type that extends AtomLike
Parameters
init
The initial value or a function that returns the initial value based on current state
AtomState<Target> | (state) => AtomState<Target>
Returns
Ext<Target>
An extension that can be applied to an atom
Examples
const something = reatomSomething().extend( withInit((initState) => ({ ...initState, ...additions })), )const myData = atom(null, 'myData') if (meta.env.TEST) { myData.extend(withInit(mockData)) }withInitHook()
withInitHook<
Target>(hook):Ext<Target>
Defined in: withInit.ts:95
Extension that runs the passed hook when the atom is initialized.
Type Parameters
Target
Target extends AtomLike<any, any[], any>
The atom type that extends AtomLike
Parameters
hook
(initState) => any
A function to be called with the initial state during initialization
Returns
Ext<Target>
An extension that can be applied to an atom
Example
const userAtom = atom({ id: 1, name: 'John' }).extend( withInitHook((initState) => { // Perform any setup logic here analytics.track('user_loaded', initState) }), )withMemo()
withMemo<
Target>(isEqual):Ext<Target>
Defined in: withMemo.ts:6
Type Parameters
Target
Target extends AtomLike<any, any[], any>
Parameters
isEqual
(prevState, nextState) => boolean
Returns
Ext<Target>
withSuspense()
withSuspense<
Target>(options):Ext<Target,SuspenseExt<AtomState<Target>>>
Defined in: withSuspense.ts:113
Extension that adds suspense support to async atoms. Creates a suspended
computed atom that tracks the resolved value of promises and throws the
promise when pending (for React Suspense compatibility).
The suspended atom will:
- Return the resolved value immediately if the promise is already fulfilled
- Throw the promise if it’s still pending (allowing Suspense boundaries to catch it)
- Propagate errors if the promise is rejected
- Automatically update when the promise resolves
Type Parameters
Target
Target extends AtomLike<any, any[], any> & Partial<SuspenseExt<AtomState<Target>>>
Parameters
options
Configuration options
preserve?
boolean = false
If true, preserves the previous state when suspending instead of throwing immediately. Useful for preventing flickering in UI.
Returns
Ext<Target, SuspenseExt<AtomState<Target>>>
An extension that adds the suspended computed atom
Example
const data = computed(async () => { const response = await fetch('/api/data') return response.json() }, 'data').extend(withSuspense())
// Subscribe to resolved values subscribe(data.suspended, (value) => { console.log('Resolved:', value) })
// Use in React component with Suspense function Component() { const value = useAtom(data.suspended) // throws promise if pending return <div>{value}</div> }withSuspenseRetry()
withSuspenseRetry<
T>():Ext<T>
Defined in: withSuspenseRetry.ts:28
Creates a mixin that retries an async action when it fails coz of a suspension
This mixin wraps an async action to automatically retry it when a Promise is thrown, which indicates a suspension. It will keep retrying until the action completes successfully or throws a non-Promise error.
⚠️ Be careful with non-idempotent operations inside the action body, as they may be executed multiple times during retries. It’s recommended to carefully plan the execution logic to handle potential retries safely.
Type Parameters
T
T extends Action<unknown[], Promise<unknown>>
Returns
Ext<T>
The same passed action
Example
const fetchUserBooks = action(async () => { const id = user().id // `user` is a suspended atom const response = await fetch(`/api/users/${id}/books`) return response.json() }).extend(withSuspenseRetry())