Skip to content

Custom operator

All atoms and actions by default include the pipe operator for easy extending and composing. You can create your own operators to enhance your code and make it more modular.

We assume that you’ve already read the core docs.

Prefix and types


Operator for the pipe function should starts with a verb.

import { Atom, CtxSpy } from '@reatom/core'
declare function mapState<T, Res>(
mapper: Fn<[CtxSpy, T, undefined | T], Res>,
name?: string,
): (anAtom: Atom<T>) => Atom<Res>

Real mapState

If an operator doesn’t create a new atom and instead mutates the passed one, you should use the with prefix.

import { Atom, AtomState } from '@reatom/core'
declare function withStateHistory<T extends Atom>(
length: string,
): (anAtom: T) => T & {
stateHistoryAtom: Atom<AtomState<T>>

We use T extends Atom instead of the simpler <T>(length: string): (anAtom: Atom<T>) => Atom<T> & {...} to preserve all additional properties added by previous operators.

Real historyAtom




import { action, atom, Atom } from '@reatom/core'
// operator accepts an options by a first argument
// and returns function witch accepts target atom
export const delay =
<T>(ms: number) =>
(anAtom: Atom<T>) => {
// to improve debugability compute name of the new atom
const name = `${}.delay`
// hide unnecessary meta by `_` prefix in name
const update = action<T>(`${name}._update`)
const updateTimeout = atom(-1, `${name}._updateTimeout`)
return atom((ctx, prevState?: T) => {
const state = ctx.spy(anAtom)
// more about action spying:
const updates = ctx.spy(update)
// first call, no need to delay
if (prevState === undefined) return state
// update from the schedule below
if (updates.length) return!.payload
// do not forget to schedule all side effects!
ctx.schedule(() => {
const timeout = setTimeout(() => update(ctx, state), ms)
updateTimeout(ctx, timeout)
return prevState
}, name)

Real debounce