### Install jotai-effect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Install the jotai-effect package using npm. ```bash npm install jotai-effect ``` -------------------------------- ### Conditionally Running Effects Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Explains that `atomEffect` only runs when it is mounted. This example shows an atom that conditionally accesses an effect based on `isEnabledAtom`. ```javascript atom((get) => { if (get(isEnabledAtom)) { get(effectAtom) } }) ``` -------------------------------- ### `withAtomEffect` for interdependent computed atoms Source: https://context7.com/jotaijs/jotai-effect/llms.txt Ideal for bidirectional computed relationships where atoms mutually derive from each other. Use `get.peek` to break circular dependencies by reading without subscribing. This example demonstrates a price/discount relationship. ```typescript import { atom, createStore } from 'jotai/vanilla' import { withAtomEffect } from 'jotai-effect' const unitPriceAtom = withAtomEffect(atom(100), (get, set) => { const unitPrice = get(unitPriceAtom) // recompute price from current discount, recompute discount from new price set(priceAtom, unitPrice * (1 - get.peek(discountAtom) / 100)) set(discountAtom, ((unitPrice - get.peek(priceAtom)) / unitPrice) * 100) }) const discountAtom = withAtomEffect(atom(0), (get, set) => { const discount = get(discountAtom) if (discount === 20 || discount === 80) { set(priceAtom, get.peek(unitPriceAtom) * (1 - discount / 100)) } }) const priceAtom = withAtomEffect(atom(100), (get, set) => { set(discountAtom, ((get.peek(unitPriceAtom) - get(priceAtom)) / get.peek(unitPriceAtom)) * 100) }) const store = createStore() const summary = atom((get) => ({ unitPrice: get(unitPriceAtom), discount: get(discountAtom), price: get(priceAtom), })) store.sub(summary, () => {}) store.set(discountAtom, 20) console.log(store.get(summary)) // { unitPrice: 100, discount: 20, price: 80 } store.set(priceAtom, 50) console.log(store.get(summary)) // { unitPrice: 100, discount: 50, price: 50 } ``` -------------------------------- ### Scoped Store Observation with React `Provider` Source: https://context7.com/jotaijs/jotai-effect/llms.txt When using a custom store with React, ensure effects target the correct store by passing the same store instance to both `observe` and the Jotai ``. The effect is started before the React tree mounts and cleaned up when the component unmounts. ```tsx import { createStore } from 'jotai/vanilla' import { Provider, useAtomValue } from 'jotai/react' import { atom } from 'jotai' import { observe } from 'jotai-effect' import { useEffect } from 'react' const themeAtom = atom<'light' | 'dark'>('light') const store = createStore() // Start observing before the React tree mounts const unobserve = observe((get) => { document.body.dataset.theme = get(themeAtom) }, store) function ThemeToggle() { const [theme, setTheme] = useAtom(themeAtom) return ( ) } function App() { useEffect(() => unobserve, []) // cleanup when App unmounts return ( ) } ``` -------------------------------- ### Sync Dependency in atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Atoms accessed with `get` inside the effect function are automatically added as dependencies. The effect re-runs when these dependencies change. ```javascript atomEffect((get, set) => { // updates whenever `anAtom` changes value get(anAtom) }) ``` -------------------------------- ### Cleanup Dependency in atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Atoms accessed with `get` inside the cleanup function do not add dependencies. The effect's dependencies are determined solely by the main effect logic. ```javascript atomEffect((get, set) => { return () => { // does not add `anAtom` as a dependency get(anAtom) } }) ``` -------------------------------- ### atomEffect(effect) — standalone reactive side-effect atom Source: https://context7.com/jotaijs/jotai-effect/llms.txt Creates a read-only Jotai atom that runs an effect function whenever atoms accessed via `get` change. The effect activates on mount and deactivates on unmount. It supports cleanup functions and idempotent execution. ```APIDOC ## atomEffect(effect) ### Description Creates a read-only Jotai atom whose sole purpose is to run `effect` whenever any of the atoms accessed with `get` inside it change. The effect activates when the atom is mounted (subscribed to or read inside a mounted derived atom) and deactivates on unmount. It is idempotent: no matter how many times the returned atom is referenced in the store, the effect executes only once per flush cycle. ### Parameters - `effect` (function): A function that receives `get` and `set` as arguments. It can optionally return a cleanup function. ### Request Example ```ts import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const countAtom = atom(0) const logAtom = atom([]) const logEffect = atomEffect((get, set) => { const count = get(countAtom) set(logAtom, (prev) => [...prev, `count is ${count}`]) return () => { set(logAtom, (prev) => [...prev, 'cleanup']) } }) const store = createStore() const unsub = store.sub(logEffect, () => {}) store.set(countAtom, (v) => v + 1) unsub() console.log(store.get(logAtom)) ``` ### Response #### Success Response - Returns a read-only Jotai atom. #### Response Example ```ts // Example output after execution: // ['count is 0', 'cleanup', 'count is 1', 'cleanup'] ``` ``` -------------------------------- ### Async Dependency in atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Asynchronous `get` calls within the effect, such as those inside `setTimeout`, do not add dependencies. The effect will not re-run based on changes to atoms accessed this way. ```javascript atomEffect((get, set) => { setTimeout(() => { // does not add `anAtom` as a dependency get(anAtom) }) }) ``` -------------------------------- ### TypeScript `Effect` Type Signature Source: https://context7.com/jotaijs/jotai-effect/llms.txt The `Effect` type signature is shared across Jotai effect APIs. It provides `get` with `.peek` for non-subscribing reads and `set` with `.recurse` for synchronous self-triggering rewrites. The effect can optionally return a `Cleanup` function. ```typescript import type { Effect } from 'jotai-effect' import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const requestAtom = atom(null) const statusAtom = atom<'idle' | 'loading' | 'done'>('idle') // Fully-typed effect with cleanup const fetchEffect: Effect = (get, set) => { const url = get(requestAtom) if (!url) return const controller = new AbortController() set(statusAtom, 'loading') fetch(url, { signal: controller.signal }) .then(() => set(statusAtom, 'done')) .catch(() => {}) // aborted fetches are silently ignored return () => { controller.abort() // cancel in-flight request on re-run or unmount set(statusAtom, 'idle') } } const store = createStore() store.sub(atomEffect(fetchEffect), () => {}) store.set(requestAtom, 'https://api.example.com/data') // statusAtom → 'loading', fetch begins store.set(requestAtom, 'https://api.example.com/other') // cleanup aborts previous fetch, new fetch starts ``` -------------------------------- ### Effect Type Source: https://context7.com/jotaijs/jotai-effect/llms.txt TypeScript signature for effect functions used in jotai-effect. All three APIs (`withAtomEffect`, `observe`, `atomEffect`) share the same `Effect` type. `get` includes `.peek` for non-subscribing reads; `set` includes `.recurse` for synchronous self-triggering rewrites. The effect may optionally return a `Cleanup` function. ```APIDOC ## Effect Type ### Description TypeScript signature for effect functions used in jotai-effect. All three APIs (`withAtomEffect`, `observe`, `atomEffect`) share the same `Effect` type. `get` includes `.peek` for non-subscribing reads; `set` includes `.recurse` for synchronous self-triggering rewrites. The effect may optionally return a `Cleanup` function. ### Type Signature ```ts import type { Effect } from 'jotai-effect' type Effect = ( get: { (atom: Atom) => Value; (atom: Atom) => Value; (atom: Atom) => Value; peek: (atom: Atom) => Value; }, set: { (atom: WritableAtom, ...args: Args) => Result; recurse: (atom: WritableAtom, ...args: Args) => Result; } ) => Cleanup | void; type Cleanup = () => void; ``` ### Example Usage ```ts import type { Effect } from 'jotai-effect' import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const requestAtom = atom(null) const statusAtom = atom<'idle' | 'loading' | 'done'>('idle') // Fully-typed effect with cleanup const fetchEffect: Effect = (get, set) => { const url = get(requestAtom) if (!url) return const controller = new AbortController() set(statusAtom, 'loading') fetch(url, { signal: controller.signal }) .then(() => set(statusAtom, 'done')) .catch(() => {}) // aborted fetches are silently ignored return () => { controller.abort() // cancel in-flight request on re-run or unmount set(statusAtom, 'idle') } } const store = createStore() store.sub(atomEffect(fetchEffect), () => {}) store.set(requestAtom, 'https://api.example.com/data') // statusAtom → 'loading', fetch begins store.set(requestAtom, 'https://api.example.com/other') // cleanup aborts previous fetch, new fetch starts ``` ``` -------------------------------- ### atomEffect with React — mounting via useAtom / useAtomValue Source: https://context7.com/jotaijs/jotai-effect/llms.txt Demonstrates how to use `atomEffect` within React components. The effect remains active as long as the component using the atom is mounted. Using `useAtomValue` is sufficient to mount the effect. ```APIDOC ## atomEffect with React ### Description Inside React, the effect stays active for as long as any component using the atom remains mounted. Using `useAtomValue` (or `useAtom`) is sufficient to mount the effect atom; its void return value does not affect rendering. ### Parameters - `effect` (function): A function that receives `get` and `set` as arguments. It can optionally return a cleanup function. ### Request Example ```tsx import { atom } from 'jotai' import { useAtomValue, useAtom } from 'jotai/react' import { atomEffect } from 'jotai-effect' const countAtom = atom(0) const persistEffect = atomEffect((get, set) => { const count = get(countAtom) localStorage.setItem('count', String(count)) return () => { localStorage.removeItem('count') } }) function CounterWithPersistence() { useAtomValue(persistEffect) // mounts the effect for the lifetime of this component const [count, setCount] = useAtom(countAtom) return } ``` ``` -------------------------------- ### Synchronous Execution with atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Demonstrates how `atomEffect` runs synchronously after other synchronous evaluations complete. It logs the count before and after an update. ```javascript const logCounts = atomEffect((get, set) => { set(logAtom, `count is ${get(countAtom)}`) }) const actionAtom = atom(null, (get, set) => { get(logAtom) // 'count is 0' set(countAtom, (value) => value + 1) // effect runs synchronously get(logAtom) // 'count is 1' }) store.sub(logCounts, () => {}) store.set(actionAtom) ``` -------------------------------- ### observe Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Mounts an effect to watch state changes on a Jotai store. Returns a function to unmount the effect. ```APIDOC ## observe ### Description Mounts an `effect` to watch state changes on a Jotai `store`. It's useful for running global side effects or logic at the store level. ### Signature ```ts type Cleanup = () => void type Effect = ( get: Getter & { peek: Getter }, set: Setter & { recurse: Setter } ) => Cleanup | void type Unobserve = () => void function observe(effect: Effect, store?: Store): Unobserve ``` ### Parameters - **effect** (Effect) - A function for observing and reacting to atom state changes. - **store** (Store, optional) - A Jotai store to mount the effect on. Defaults to the global store if not provided. ### Returns - **Unobserve** - A stable function that removes the effect from the store and cleans up any internal references. ### Usage ```js import { observe } from 'jotai-effect' const unobserve = observe((get, set) => { set(logAtom, `someAtom changed: ${get(someAtom)}`) }) unobserve() ``` ### Usage With React Pass the store to both `observe` and the `Provider` to ensure the effect is mounted to the correct store. ```tsx const store = createStore() const unobserve = observe((get, set) => { set(logAtom, `someAtom changed: ${get(someAtom)}`) }, store) ... ``` ``` -------------------------------- ### Batched Updates with atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Shows how `atomEffect` batches multiple synchronous updates into a single atomic transaction. The resulting array `combos` reflects the batched updates. ```javascript const tensAtom = atom(0) const onesAtom = atom(0) const updateTensAndOnes = atom(null, (get, set) => { set(tensAtom, (value) => value + 1) set(onesAtom, (value) => value + 1) }) const combos = atom([]) const effectAtom = atomEffect((get, set) => { const value = get(tensAtom) * 10 + get(onesAtom) set(combos, (arr) => [...arr, value]) }) store.sub(effectAtom, () => {}) store.set(updateTensAndOnes) store.get(combos) // [00, 11] ``` -------------------------------- ### Observe Global Store Changes Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Use `observe` to mount a global effect that watches state changes on a Jotai store. This is useful for application-wide side effects. The returned function `unobserve` can be called to remove the effect. ```javascript import { observe } from 'jotai-effect' const unobserve = observe((get, set) => { set(logAtom, `someAtom changed: ${get(someAtom)}`) }) unobserve() ``` -------------------------------- ### observe Source: https://context7.com/jotaijs/jotai-effect/llms.txt Mounts an effect directly on a Jotai store without needing any atom reference. It is the simplest API for application-level side effects that run outside the React component lifecycle. Calling `observe` with the same effect function and store more than once returns the same stable unsubscribe handle and does not create duplicate subscriptions. ```APIDOC ## observe(effect, store?) ### Description Mounts an effect directly on a Jotai store without needing any atom reference. It is the simplest API for application-level side effects that run outside the React component lifecycle. Calling `observe` with the same effect function and store more than once returns the same stable unsubscribe handle and does not create duplicate subscriptions. ### Parameters - **effect** (Effect) - The effect function to observe. - **store** (Store, optional) - The Jotai store to observe. If not provided, the default store is used. ### Usage ```ts import { atom, createStore } from 'jotai/vanilla' import { observe } from 'jotai-effect' const userAtom = atom({ name: 'Alice', role: 'admin' }) const auditLogAtom = atom([]) const store = createStore() // Runs immediately and re-runs whenever userAtom changes const unobserve = observe((get, set) => { const user = get(userAtom) set(auditLogAtom, (log) => [ ...log, `[${new Date().toISOString()}] active user: ${user.name} (${user.role})`, ]) return () => { // optional cleanup before re-run or final teardown console.log('observer cleanup') } }, store) store.set(userAtom, { name: 'Bob', role: 'viewer' }) console.log(store.get(auditLogAtom)) unobserve() // stops the observation and runs cleanup ``` ### Usage with React Provider When using a custom store with React, pass the same store instance to both `observe` and the Jotai `` to ensure the effect targets the correct store. ```tsx import { createStore } from 'jotai/vanilla' import { Provider, useAtomValue } from 'jotai/react' import { atom } from 'jotai' import { observe } from 'jotai-effect' import { useEffect } from 'react' const themeAtom = atom<'light' | 'dark'>('light') const store = createStore() // Start observing before the React tree mounts const unobserve = observe((get) => { document.body.dataset.theme = get(themeAtom) }, store) function ThemeToggle() { const [theme, setTheme] = useAtom(themeAtom) return ( ) } function App() { useEffect(() => unobserve, []) // cleanup when App unmounts return ( ) } ``` ``` -------------------------------- ### Recursion Support with set.recurse Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Demonstrates recursion support in `atomEffect` using `set.recurse`. Note that recursion is not supported within the cleanup function. ```javascript atomEffect((get, set) => { const count = get(countAtom) if (count % 10 === 0) { return } set.recurse(countAtom, (value) => value + 1) }) ``` -------------------------------- ### `withAtomEffect` to bind effects to existing atoms Source: https://context7.com/jotaijs/jotai-effect/llms.txt Creates a new atom that is a clone of `targetAtom` with `effect` attached to its lifecycle. The effect is active while the returned atom is mounted and preserves the writability of the original atom without modifying it. ```typescript import { atom, createStore } from 'jotai/vanilla' import { withAtomEffect } from 'jotai-effect' const priceAtom = atom(100) const taxAtom = atom(0) // Automatically compute tax whenever price changes const priceWithTaxEffect = withAtomEffect(priceAtom, (get, set) => { const price = get(priceWithTaxEffect) // self-reference for dependency set(taxAtom, price * 0.2) }) const store = createStore() store.sub(priceWithTaxEffect, () => {}) console.log(store.get(priceAtom)) // 100 console.log(store.get(taxAtom)) // 20 store.set(priceWithTaxEffect, 200) // writable — original atom type preserved console.log(store.get(priceAtom)) // 200 console.log(store.get(taxAtom)) // 40 ``` -------------------------------- ### Using get.peek in atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Illustrates how to use `get.peek` within `atomEffect` to read atom data without subscribing to it. This prevents the atom from being added as a dependency. ```javascript const countAtom = atom(0) atomEffect((get, set) => { const count = get.peek(countAtom) // Will not add `countAtom` as a dependency }) ``` -------------------------------- ### withAtomEffect Source: https://context7.com/jotaijs/jotai-effect/llms.txt Chains effects to atoms, ensuring predictable execution order based on dependency. Effects run in dependency order (parent before child) on every flush. ```APIDOC ## withAtomEffect ### Description Chains effects to atoms, ensuring predictable execution order based on dependency. Effects run in dependency order (parent before child) on every flush. ### Usage ```ts import { atom, createStore } from 'jotai/vanilla' import { withAtomEffect } from 'jotai-effect' const order: string[] = [] const base = atom(0) const a = withAtomEffect(base, (get) => { get(a); order.push('a') }) const b = withAtomEffect(a, (get) => { get(b); order.push('b') }) const c = withAtomEffect(b, (get) => { get(c); order.push('c') }) const store = createStore() store.sub(a, () => {}) store.sub(b, () => {}) store.sub(c, () => {}) order.length = 0 store.set(base, (v) => v + 1) console.log(order.join('')) // 'abc' ``` ``` -------------------------------- ### withAtomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Binds an effect to a clone of the target atom. The effect is active while the cloned atom is mounted. ```APIDOC ## withAtomEffect ### Description `withAtomEffect` binds an effect to a clone of the target atom. The effect is active while the cloned atom is mounted. ### Signature ```ts function withAtomEffect(targetAtom: Atom, effect: Effect): Atom ``` ### Parameters - **targetAtom** (Atom) - The atom to which the effect is bound. - **effect** (Effect) - A function for observing and reacting to atom state changes. ### Returns - **Atom** - An atom that is equivalent to the target atom but having a bound effect. ### Usage ```js import { withAtomEffect } from 'jotai-effect' const valuesAtom = withAtomEffect(atom(null), (get, set) => { set(valuesAtom, get(countAtom)) return () => { // cleanup } }) ``` ``` -------------------------------- ### atomEffect with get.peek — non-subscribing reads Source: https://context7.com/jotaijs/jotai-effect/llms.txt Explains how to use `get.peek` within an `atomEffect` to read an atom's value without creating a subscription dependency. This prevents the effect from re-running when the peeked atom changes. ```APIDOC ## atomEffect with get.peek ### Description `get.peek` reads an atom's current value without adding it to the effect's dependency set. The effect will not re-run when that atom changes. ### Parameters - `effect` (function): A function that receives `get` (with `.peek` method) and `set` as arguments. ### Request Example ```ts import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const triggerAtom = atom(0) const metaAtom = atom({ user: 'alice' }) const reportEffect = atomEffect((get, set) => { const trigger = get(triggerAtom) const meta = get.peek(metaAtom) console.log(`trigger=${trigger}, user=${meta.user}`) }) const store = createStore() store.sub(reportEffect, () => {}) store.set(metaAtom, { user: 'bob' }) store.set(triggerAtom, (v) => v + 1) ``` ### Response #### Success Response - Logs output to the console based on atom values. #### Response Example ```ts // Output from the example: trigger=0, user=alice trigger=1, user=bob ``` ``` -------------------------------- ### Cleanup Function in atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Demonstrates the use of a cleanup function within `atomEffect`. The returned function is invoked on unmount or before the effect re-evaluates, useful for clearing intervals or subscriptions. ```javascript atomEffect((get, set) => { const intervalId = setInterval(() => set(clockAtom, Date.now())) return () => clearInterval(intervalId) }) ``` -------------------------------- ### Bind Effect to Atom with withAtomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Use `withAtomEffect` to bind an effect to a clone of a target atom. The effect is active as long as the cloned atom is mounted. The effect function can return a cleanup function. ```javascript import { withAtomEffect } from 'jotai-effect' const valuesAtom = withAtomEffect(atom(null), (get, set) => { set(valuesAtom, get(countAtom)) return () => { // cleanup } }) ``` -------------------------------- ### Observe Specific Store Changes with React Provider Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md When not using the default store, pass the specific store to both `observe` and the `Provider` to ensure the effect is mounted to the correct store. The returned function `unobserve` can be called to remove the effect. ```tsx import { observe } from 'jotai-effect' import { createStore } from 'jotai' import { Provider } from 'jotai' const store = createStore() const unobserve = observe((get, set) => { set(logAtom, `someAtom changed: ${get(someAtom)}`) }, store) ... ``` -------------------------------- ### Store-Level Observation with `observe` Source: https://context7.com/jotaijs/jotai-effect/llms.txt Employ `observe` for application-level side effects that run outside React. It mounts an effect directly on a Jotai store. Calling `observe` multiple times with the same effect and store returns a stable unsubscribe handle. ```typescript import { atom, createStore } from 'jotai/vanilla' import { observe } from 'jotai-effect' const userAtom = atom({ name: 'Alice', role: 'admin' }) const auditLogAtom = atom([]) const store = createStore() // Runs immediately and re-runs whenever userAtom changes const unobserve = observe((get, set) => { const user = get(userAtom) set(auditLogAtom, (log) => [ ...log, `[${new Date().toISOString()}] active user: ${user.name} (${user.role})`, ]) return () => { // optional cleanup before re-run or final teardown console.log('observer cleanup') } }, store) store.set(userAtom, { name: 'Bob', role: 'viewer' }) console.log(store.get(auditLogAtom)) // [ // '[2024-...] active user: Alice (admin)', // '[2024-...] active user: Bob (viewer)', // ] unobserve() // stops the observation and runs cleanup ``` -------------------------------- ### Chained Effects with `withAtomEffect` Source: https://context7.com/jotaijs/jotai-effect/llms.txt Use `withAtomEffect` to bind effects to a chain of atoms. Effects execute in dependency order (parent before child) on every flush. Ensure all dependencies are correctly declared. ```typescript import { atom, createStore } from 'jotai/vanilla' import { withAtomEffect } from 'jotai-effect' const order: string[] = [] const base = atom(0) const a = withAtomEffect(base, (get) => { get(a); order.push('a') }) const b = withAtomEffect(a, (get) => { get(b); order.push('b') }) const c = withAtomEffect(b, (get) => { get(c); order.push('c') }) const store = createStore() store.sub(a, () => {}) store.sub(b, () => {}) store.sub(c, () => {}) order.length = 0 store.set(base, (v) => v + 1) console.log(order.join('')) // 'abc' ``` -------------------------------- ### Mount atomEffect in React using useAtomValue Source: https://context7.com/jotaijs/jotai-effect/llms.txt In React, an atomEffect remains active as long as any component using it is mounted. Use useAtomValue (or useAtom) to mount the effect; its return value does not impact rendering. ```tsx import { atom } from 'jotai' import { useAtomValue, useAtom } from 'jotai/react' import { atomEffect } from 'jotai-effect' const countAtom = atom(0) // Sync count to localStorage whenever it changes const persistEffect = atomEffect((get, set) => { const count = get(countAtom) localStorage.setItem('count', String(count)) return () => { localStorage.removeItem('count') } }) function CounterWithPersistence() { useAtomValue(persistEffect) // mounts the effect for the lifetime of this component const [count, setCount] = useAtom(countAtom) return } ``` -------------------------------- ### atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Creates an atom for declaring side effects that react to state changes when mounted. ```APIDOC ## atomEffect ### Description `atomEffect` creates an atom for declaring side effects that react to state changes when mounted. ### Signature ```ts function atomEffect(effect: Effect): Atom ``` ### Parameters - **effect** (Effect) - A function for observing and reacting to atom state changes. ### Returns - **Atom** - An atom that triggers the effect when mounted. ### Usage ```js import { atomEffect } from 'jotai-effect' const logEffect = atomEffect((get, set) => { set(logAtom, get(someAtom)) // Runs on mount or when someAtom changes return () => { set(logAtom, 'unmounting') // Cleanup on unmount } }) // activates the atomEffect while Component is mounted function Component() { useAtom(logEffect) } ``` ``` -------------------------------- ### Create Atom for Side Effects with atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Use `atomEffect` to create an atom that declares side effects reacting to state changes when mounted. The effect function can return a cleanup function that runs on unmount. ```javascript import { atomEffect } from 'jotai-effect' const logEffect = atomEffect((get, set) => { set(logAtom, get(someAtom)) // Runs on mount or when someAtom changes return () => { set(logAtom, 'unmounting') // Cleanup on unmount } }) // activates the atomEffect while Component is mounted function Component() { useAtom(logEffect) } ``` -------------------------------- ### `atomEffect` with `set.recurse` for synchronous self-recursion Source: https://context7.com/jotaijs/jotai-effect/llms.txt Use `set.recurse` to write to an atom and re-evaluate the effect synchronously within the same flush. This allows state to be driven to a stable target value without waiting for the next tick. Recursion is forbidden inside cleanup functions. ```typescript import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const valueAtom = atom(0) // Clamp value to the nearest multiple of 10 const clampEffect = atomEffect((get, set) => { const v = get(valueAtom) if (v % 10 !== 0) { // recurse immediately: effect re-runs until the condition is false set.recurse(valueAtom, Math.round(v / 10) * 10) } }) const store = createStore() store.sub(clampEffect, () => {}) store.set(valueAtom, 23) console.log(store.get(valueAtom)) // 20 (clamped synchronously) store.set(valueAtom, 57) console.log(store.get(valueAtom)) // 60 (clamped synchronously) ``` -------------------------------- ### Create and manage a standalone reactive side-effect atom with atomEffect Source: https://context7.com/jotaijs/jotai-effect/llms.txt Use atomEffect to create a read-only atom that runs a side effect when accessed atoms change. Effects activate on mount and deactivate on unmount. A cleanup function can be returned to run before re-evaluation or on unmount. ```typescript import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const countAtom = atom(0) const logAtom = atom([]) // Effect runs on mount and re-runs whenever countAtom changes. // Returns a cleanup function that runs before each re-run and on unmount. const logEffect = atomEffect((get, set) => { const count = get(countAtom) // subscribes — effect re-runs when count changes set(logAtom, (prev) => [...prev, `count is ${count}`]) return () => { // cleanup: runs before next effect execution or on unmount set(logAtom, (prev) => [...prev, 'cleanup']) } }) const store = createStore() const unsub = store.sub(logEffect, () => {}) // mounts the effect → runs immediately // count is 0 → log: ['count is 0'] store.set(countAtom, (v) => v + 1) // cleanup runs, then effect re-runs // log: ['count is 0', 'cleanup', 'count is 1'] unsub() // cleanup runs one final time on unmount // log: ['count is 0', 'cleanup', 'count is 1', 'cleanup'] console.log(store.get(logAtom)) // ['count is 0', 'cleanup', 'count is 1', 'cleanup'] ``` -------------------------------- ### Avoiding Infinite Loops with atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Illustrates how `atomEffect` prevents infinite loops by not rerunning when it updates a value it is already watching. This is crucial for self-updating atoms. ```javascript atomEffect((get, set) => { get(countAtom) set(countAtom, (value) => value + 1) // Will not loop }) ``` -------------------------------- ### Use get.peek for non-subscribing reads in atomEffect Source: https://context7.com/jotaijs/jotai-effect/llms.txt Employ get.peek within an atomEffect to read an atom's value without creating a dependency. The effect will not re-run when atoms accessed via get.peek change. ```typescript import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const triggerAtom = atom(0) const metaAtom = atom({ user: 'alice' }) const reportEffect = atomEffect((get, set) => { // effect re-runs only when triggerAtom changes const trigger = get(triggerAtom) // metaAtom is read without creating a dependency const meta = get.peek(metaAtom) console.log(`trigger=${trigger}, user=${meta.user}`) }) const store = createStore() store.sub(reportEffect, () => {}) // prints: trigger=0, user=alice store.set(metaAtom, { user: 'bob' }) // does NOT re-run the effect store.set(triggerAtom, (v) => v + 1) // re-runs: trigger=1, user=bob ``` -------------------------------- ### `atomEffect` for batched synchronous updates Source: https://context7.com/jotaijs/jotai-effect/llms.txt When multiple atoms are updated synchronously in a single write atom, jotai-effect coalesces them into one effect execution. This prevents intermediate renders or redundant effect calls. The effect sees the final values after all batched updates. ```typescript import { atom, createStore } from 'jotai/vanilla' import { atomEffect } from 'jotai-effect' const tensAtom = atom(1) const onesAtom = atom(1) const historyAtom = atom([]) // Runs once per flush even when both tensAtom and onesAtom change together const trackEffect = atomEffect((get, set) => { const value = get(tensAtom) * 10 + get(onesAtom) set(historyAtom, (h) => [...h, value]) }) const batchWrite = atom(null, (_get, set) => { set(tensAtom, (v) => v + 1) // \ set(onesAtom, (v) => v + 1) // } batched — effect sees final values }) const store = createStore() store.sub(trackEffect, () => {}) // initial run: history = [11] store.set(batchWrite) // single effect run: history = [11, 22] (not [11, 21, 22]) console.log(store.get(historyAtom)) // [11, 22] ``` -------------------------------- ### Dependency Map Recalculation in atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md The dependency map for an effect is recalculated on every run. This allows for dynamic dependencies based on conditional logic within the effect. ```javascript atomEffect((get, set) => { if (get(isEnabledAtom)) { // `isEnabledAtom` and `anAtom` are dependencies const aValue = get(anAtom) } else { // `isEnabledAtom` and `anotherAtom` are dependencies const anotherValue = get(anotherAtom) } }) ``` -------------------------------- ### Idempotency of atomEffect Source: https://github.com/jotaijs/jotai-effect/blob/main/README.md Shows that `atomEffect` runs only once per state change, even if subscribed to multiple times. The counter `i` increments only once after the state change. ```javascript let i = 0 const effectAtom = atomEffect(() => { get(countAtom) i++ }) store.sub(effectAtom, () => {}) store.sub(effectAtom, () => {}) store.set(countAtom, (value) => value + 1) console.log(i) // 1 ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.