Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/reatom/reatom/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The withInit extension allows you to define a dynamically computed initial value for an atom. While you can typically use an init callback in the atom’s first argument (atom(() => new Date())), withInit is useful when you need to add an initial callback after atom creation or compose multiple initialization steps.

Type Signature

function withInit<Target extends AtomLike>(
  init: AtomState<Target> | ((state: AtomState<Target>, ...params: any[]) => AtomState<Target>)
): Ext<Target>

function withInitHook<Target extends AtomLike>(
  hook: (initState: AtomState<Target>) => any,
  queue?: QueueKind
): Ext<Target>

function isInit(): boolean

Parameters

init
State | (state, ...params) => State
required
The initial value or a function that returns the initial value based on current state.As value: Direct initial state
As function: Receives current state and any init params, returns new initial state

Return Value

Returns an extension that sets the initial state on first evaluation.

Examples

Basic Init Value

import { atom } from '@reatom/core'
import { withInit } from '@reatom/core/extensions'

const data = atom(0, 'data').extend(withInit(1))

console.log(data()) // → 1

Init with Callback

const data = atom(0, 'data').extend(
  withInit(() => Math.random())
)

console.log(data()) // → random number (e.g., 0.742)
data() // → same random number (callback only runs once)

Compose Initial State

const something = reatomSomething().extend(
  withInit((initState) => ({
    ...initState,
    additionalField: 'value',
  }))
)

Conditional Init

const myData = atom(null, 'myData')

if (import.meta.env.TEST) {
  myData.extend(withInit(mockData))
}

Multiple Init Extensions

const data = atom(0, 'data').extend(
  withInit(() => 1),
  withInit((state) => state + 10)
)

console.log(data()) // → 11
// First init: 0 → 1
// Second init: 1 → 11

Different Contexts

import { context, clearStack } from '@reatom/core'

let i = 0
const data = atom(0, 'data').extend(
  withInit(() => i++)
)

console.log(data()) // → 0
console.log(data()) // → 0 (same context)

clearStack()

console.log(context.start(() => data())) // → 1 (new context)
console.log(context.start(() => data())) // → 2 (another new context)

context.start(() => {
  console.log(data()) // → 3
  console.log(data()) // → 3 (same context)
  
  console.log(context.start(() => data())) // → 4 (nested context)
})

Reusable Init Extension

const timestampInit = withInit(() => Date.now())

const a1 = atom(0, 'a1').extend(timestampInit)
const a2 = atom(0, 'a2').extend(timestampInit)

console.log(a1()) // → 1709467200000
console.log(a2()) // → 0 (extension only runs for first atom)

Init with Parameters

const data = atom(0, 'data').extend(
  withInit((initState, multiplier = 2) => initState * multiplier)
)

// Note: params only available in specific contexts
// This is an advanced use case

Cycle Breaking

const state = atom(0).extend(
  withInit((init, ...params) => 
    params.length ? init : initState()
  )
)

const initState = atom(0).extend(
  withInit((init, ...params) => 
    params.length ? init : state()
  )
)

// Both can reference each other without infinite loop
console.log(state())     // → 0
console.log(initState()) // → 0

initState.set(5)
console.log(state())     // → 5

withInitHook

Runs a callback during initialization without modifying the state:
const userAtom = atom({ id: 1, name: 'John' }).extend(
  withInitHook((initState) => {
    analytics.track('user_loaded', initState)
  })
)

Init Hook with Queue

const data = atom(0).extend(
  withInitHook(
    (state) => {
      console.log('Init state:', state)
    },
    'effect' // Queue kind
  )
)

isInit Helper

Checks if currently in the initialization phase:
import { isInit } from '@reatom/core/extensions'

const search = atom('', 'search').extend(withSearchParams('search'))

const page = atom(1, 'page').extend(
  withSearchParams('page'),
  withComputed((state) => {
    search() // subscribe to search changes
    // Do NOT drop the persisted state on init
    return isInit() ? state : 1
  })
)

Use Cases

Random ID Generation

const generateId = () => 
  Math.random().toString(36).substring(7)

const item = atom({ name: '' }, 'item').extend(
  withInit((state) => ({
    ...state,
    id: generateId(),
  }))
)

Timestamp Creation

const record = atom({ value: 0 }, 'record').extend(
  withInit((state) => ({
    ...state,
    createdAt: Date.now(),
  }))
)

Load from LocalStorage

const preferences = atom(
  { theme: 'light' },
  'preferences'
).extend(
  withInit(() => {
    const stored = localStorage.getItem('preferences')
    return stored ? JSON.parse(stored) : { theme: 'light' }
  })
)

Merge Default Config

const defaultConfig = {
  timeout: 5000,
  retries: 3,
  cache: true,
}

const config = atom({ timeout: 10000 }, 'config').extend(
  withInit((state) => ({
    ...defaultConfig,
    ...state,
  }))
)

console.log(config())
// → { timeout: 10000, retries: 3, cache: true }

Environment-Specific Initialization

const apiUrl = atom('', 'apiUrl').extend(
  withInit(() => {
    if (import.meta.env.PROD) {
      return 'https://api.production.com'
    } else if (import.meta.env.DEV) {
      return 'http://localhost:3000'
    } else {
      return 'https://api.staging.com'
    }
  })
)

Feature Flag Init

const features = atom(
  { newUI: false },
  'features'
).extend(
  withInit((state) => ({
    ...state,
    newUI: localStorage.getItem('beta-features') === 'true',
  }))
)

Sequential Initialization

const data = atom({ value: 0 }, 'data').extend(
  withInit((state) => {
    console.log('First init:', state)
    return { ...state, step1: true }
  }),
  withInit((state) => {
    console.log('Second init:', state)
    return { ...state, step2: true }
  }),
  withInit((state) => {
    console.log('Third init:', state)
    return { ...state, step3: true }
  })
)

console.log(data())
// → { value: 0, step1: true, step2: true, step3: true }

Analytics on Init

const session = atom(
  { userId: null, startTime: Date.now() },
  'session'
).extend(
  withInitHook((state) => {
    analytics.track('session_started', {
      startTime: state.startTime,
    })
  })
)