Skip to main content

useAtomCallback

Creates imperative callbacks that can read and write atom values without causing component re-renders.

Import

import { useAtomCallback } from 'jotai/utils'

Signature

function useAtomCallback<Result, Args extends unknown[]>(
  callback: (get: Getter, set: Setter, ...arg: Args) => Result,
  options?: Options
): (...args: Args) => Result

Parameters

callback
(get: Getter, set: Setter, ...arg: Args) => Result
required
A callback function that receives:
  • get: Function to read atom values
  • set: Function to write atom values
  • ...arg: Additional arguments passed when the callback is invoked
options
Options
Optional configuration:
  • store: Specify a custom store to use

Returns

Returns a memoized callback function that can be called with the specified arguments. Type: (...args: Args) => Result

Usage

import { atom, useAtom } from 'jotai'
import { useAtomCallback } from 'jotai/utils'

const countAtom = atom(0)
const nameAtom = atom('John')

function UserStats() {
  const [count] = useAtom(countAtom)
  
  const logUserInfo = useAtomCallback(
    (get) => {
      const currentCount = get(countAtom)
      const currentName = get(nameAtom)
      console.log(`${currentName} has count: ${currentCount}`)
    }
  )
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={logUserInfo}>Log Info</button>
    </div>
  )
}

Async Callbacks

import { atom } from 'jotai'
import { useAtomCallback } from 'jotai/utils'

const userAtom = atom({ id: 1, name: 'John' })
const statusAtom = atom('idle')

function SaveButton() {
  const saveUser = useAtomCallback(
    async (get, set) => {
      set(statusAtom, 'saving')
      
      const user = get(userAtom)
      
      try {
        await fetch('/api/users', {
          method: 'POST',
          body: JSON.stringify(user)
        })
        set(statusAtom, 'success')
      } catch (error) {
        set(statusAtom, 'error')
      }
    }
  )
  
  return <button onClick={saveUser}>Save</button>
}

With Arguments

import { atom } from 'jotai'
import { useAtomCallback } from 'jotai/utils'

const itemsAtom = atom<string[]>([])

function ItemManager() {
  const addItem = useAtomCallback(
    (get, set, newItem: string) => {
      const items = get(itemsAtom)
      set(itemsAtom, [...items, newItem])
    }
  )
  
  const removeItem = useAtomCallback(
    (get, set, index: number) => {
      const items = get(itemsAtom)
      set(itemsAtom, items.filter((_, i) => i !== index))
    }
  )
  
  return (
    <div>
      <button onClick={() => addItem('New Item')}>Add</button>
      <button onClick={() => removeItem(0)}>Remove First</button>
    </div>
  )
}

Reading Without Re-renders

useAtomCallback is useful when you need to read atom values imperatively without subscribing to updates:
import { atom, useAtom } from 'jotai'
import { useAtomCallback } from 'jotai/utils'

const formAtom = atom({ name: '', email: '' })

function Form() {
  const [form, setForm] = useAtom(formAtom)
  
  // This callback reads the form without causing re-renders
  const validateForm = useAtomCallback(
    (get) => {
      const currentForm = get(formAtom)
      return currentForm.name.length > 0 && currentForm.email.includes('@')
    }
  )
  
  const handleSubmit = () => {
    if (validateForm()) {
      console.log('Form is valid!')
    }
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input 
        value={form.name} 
        onChange={(e) => setForm({ ...form, name: e.target.value })} 
      />
      <input 
        value={form.email} 
        onChange={(e) => setForm({ ...form, email: e.target.value })} 
      />
      <button type="submit">Submit</button>
    </form>
  )
}
  • useSetAtom - Write atoms without subscribing to updates
  • useAtomValue - Read atoms with subscriptions
  • atom - Create atoms with custom read/write logic

Build docs developers (and LLMs) love