Skip to main content
Module

x/jotai/docs/recipes/atom-with-listeners.mdx

👻 Primitive and flexible state management for React
Go to Latest
File
---title: atomWithListenersnav: 8.08keywords: creators,listeners---
> `atomWithListeners` creates an atom and a hook. The hook can be called to> add a new listener. The hook takes as an argument a callback, and that> callback is called every time the atom's value is set. The hook also> returns a function to remove the listener.This can be useful when you want to create a component that can listen to whenan atom's state changes without having to re-render that component with each ofthose state changes.
```tsimport { useEffect } from 'react'import { atom, useSetAtom, Getter, Setter, SetStateAction } from 'jotai'
type Callback<Value> = ( get: Getter, set: Setter, newVal: Value, prevVal: Value,) => void
export function atomWithListeners<Value>(initialValue: Value) { const baseAtom = atom(initialValue) const listenersAtom = atom<Callback<Value>[]>([]) const anAtom = atom( (get) => get(baseAtom), (get, set, arg: SetStateAction<Value>) => { const prevVal = get(baseAtom) set(baseAtom, arg) const newVal = get(baseAtom) get(listenersAtom).forEach((callback) => { callback(get, set, newVal, prevVal) }) }, ) const useListener = (callback: Callback<Value>) => { const setListeners = useSetAtom(listenersAtom) useEffect(() => { setListeners((prev) => [...prev, callback]) return () => setListeners((prev) => { const index = prev.indexOf(callback) return [...prev.slice(0, index), ...prev.slice(index + 1)] }) }, [setListeners, callback]) } return [anAtom, useListener] as const}```
In a component:
```jsxconst [countAtom, useCountListener] = atomWithListeners(0)
function EvenCounter() { const [evenCount, setEvenCount] = useState(0) useCountListener( useCallback( (get, set, newVal, prevVal) => { // Every time `countAtom`'s value is set, we check if its new value // is even, and if it is, we increment `evenCount`. if (newVal % 2 === 0) { setEvenCount((c) => c + 1) } }, [setEvenCount], ), ) return <>Count was set to an even number {evenCount} times.</>}```