From 93d20ddfb87a1bfd9f26b3d8d43102843213be57 Mon Sep 17 00:00:00 2001 From: David Maskasky Date: Sun, 9 Feb 2025 21:06:37 -0800 Subject: [PATCH] update readme --- README.md | 131 +++++++++++++++++++-------------------- tests/atomEffect.test.ts | 4 +- 2 files changed, 65 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index eefa250..f16a736 100644 --- a/README.md +++ b/README.md @@ -139,18 +139,52 @@ const valuesAtom = withAtomEffect(atom(null), (get, set) => { ## Effect Behavior -- **Cleanup Function:** - The cleanup function is invoked on unmount or before re-evaluation. +- **Executes Synchronously:** + `effect` runs synchronous in the current task after synchronous evaluations complete.
Example ```js - atomEffect((get, set) => { - const intervalId = setInterval(() => set(clockAtom, Date.now())) - return () => clearInterval(intervalId) + const countAtom = atom(0) + const logAtom = atom('') + const logCounts = atomEffect((get, set) => { + set(logAtom, `count is now ${get(countAtom)}`) + }) + const setCountAndReadLog = atom(null, async (get, set) => { + get(logAtom) // 'count is now 0' + set(countAtom, increment) // effect runs synchronously + get(logAtom) // 'count is now 1' + }) + store.sub(logCounts, () => {}) + store.set(setCountAndReadLog) + ``` + +
+ +- **Batched Updates:** + Multiple synchronous updates are batched as a single atomic transaction. + + +
+ Example + + ```js + 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] ```
@@ -172,28 +206,24 @@ const valuesAtom = withAtomEffect(atom(null), (get, set) => { -- **Supports Recursion:** - Recursion is supported with `set.recurse` but not in cleanup. +- **Cleanup Function:** + The cleanup function is invoked on unmount or before re-evaluation.
Example ```js - const countAtom = atom(0) atomEffect((get, set) => { - const count = get(countAtom) - const timeoutId = setTimeout(() => { - set.recurse(countAtom, increment) - }, 1000) - return () => clearTimeout(timeoutId) + const intervalId = setInterval(() => set(clockAtom, Date.now())) + return () => clearInterval(intervalId) }) ```
-- **Supports Peek:** - Use `get.peek` to read atom data without subscribing. +- **Supports Recursion:** + Recursion is supported with `set.recurse` but not in cleanup.
@@ -202,14 +232,18 @@ const valuesAtom = withAtomEffect(atom(null), (get, set) => { ```js const countAtom = atom(0) atomEffect((get, set) => { - const count = get.peek(countAtom) // Will not add countAtom as a dependency + const count = get(countAtom) + const timeoutId = setTimeout(() => { + set.recurse(countAtom, increment) + }, 1000) + return () => clearTimeout(timeoutId) }) ```
-- **Executes In The Next Microtask:** - `effect` runs in the next available microtask after synchronous evaluations complete. +- **Supports Peek:** + Use `get.peek` to read atom data without subscribing.
@@ -217,48 +251,33 @@ const valuesAtom = withAtomEffect(atom(null), (get, set) => { ```js const countAtom = atom(0) - const logAtom = atom('') - const logCounts = atomEffect((get, set) => { - set(logAtom, `count is now ${get(countAtom)}`) - }) - const setCountAndReadLog = atom(null, async (get, set) => { - get(logAtom) // 'count is now 0' - set(countAtom, increment) // effect runs in next microtask - get(logAtom) // 'count is now 0' - await Promise.resolve() - get(logAtom) // 'count is now 1' + atomEffect((get, set) => { + const count = get.peek(countAtom) // Will not add countAtom as a dependency }) - store.sub(logCounts, () => {}) - store.set(setCountAndReadLog) ```
-- **Batched Updates:** - Multiple synchronous updates are batched as a single atomic transaction. +- **Idempotency:** + `atomEffect` runs once per state change, regardless of how many times it is referenced.
Example ```js - 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]) + let i = 0 + const effectAtom = atomEffect(() => { + get(countAtom) + i++ }) store.sub(effectAtom, () => {}) - store.set(updateTensAndOnes) - store.get(combos) // [00, 11] + store.sub(effectAtom, () => {}) + store.set(countAtom, increment) + console.log(i) // 1 ``` -
+ - **Conditionally Running Effects:** `atomEffect` only runs when mounted. @@ -277,28 +296,6 @@ const valuesAtom = withAtomEffect(atom(null), (get, set) => { -- **Idempotency:** - `atomEffect` runs once per state change, regardless of how many times it is referenced. - - -
- Example - - ```js - let i = 0 - const effectAtom = atomEffect(() => { - get(countAtom) - i++ - }) - store.sub(effectAtom, () => {}) - store.sub(effectAtom, () => {}) - store.set(countAtom, increment) - await Promise.resolve() - console.log(i) // 1 - ``` - -
- ## Dependency Management Aside from mount events, the effect runs when any of its dependencies change value. diff --git a/tests/atomEffect.test.ts b/tests/atomEffect.test.ts index 92ef029..c28e595 100644 --- a/tests/atomEffect.test.ts +++ b/tests/atomEffect.test.ts @@ -1050,17 +1050,15 @@ it('should not run the effect when the effectAtom is unmounted', function test() expect(runCount).toBe(0) }) -it.only('should work in StrictMode', async () => { +it('should work in StrictMode', function test() { const watchedAtom = atom(0) let runCount = 0 let cleanupCount = 0 - let deferred = createDeferred() const effectAtom = atomEffect((get, set) => { get(watchedAtom) runCount++ console.log('effect', { runCount }) - deferred.resolve() set(watchedAtom, (v) => v + 1) return () => { cleanupCount++