Skip to content

Commit 3fbe293

Browse files
committed
test: add test for subscriptionRef recomputation with derived state
Add a test to verify that subscriptionRef atoms properly recompute when their dependencies change. This test was added based on a user report that subscriptionRef atoms don't recompute when dependencies accessed via get.some() change. The test verifies that both regular derived atoms and subscriptionRef atoms correctly recompute when an Option dependency changes value.
1 parent 91aac3e commit 3fbe293

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

packages/atom/test/Atom.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,80 @@ describe("Atom", () => {
14461446

14471447
unmount2()
14481448
})
1449+
1450+
it("subscriptionRef with runtime recomputes when dependencies change", async () => {
1451+
vitest.useRealTimers()
1452+
1453+
// Test that subscriptionRef atoms properly recompute when their dependencies change
1454+
// This test was added based on a user report that subscriptionRef atoms don't recompute
1455+
// when dependencies accessed via get.some() change.
1456+
const chatIdAtom = Atom.make<Option.Option<string>>(Option.none()).pipe(Atom.keepAlive)
1457+
1458+
// A regular derived atom using Effect.fnUntraced
1459+
let derivedRecomputes = 0
1460+
const derivedStateAtom = counterRuntime.atom(
1461+
Effect.fnUntraced(function*(get: Atom.Context) {
1462+
const chatId = yield* get.some(chatIdAtom)
1463+
derivedRecomputes++
1464+
return chatId
1465+
})
1466+
)
1467+
1468+
// A subscriptionRef atom that also depends on chatIdAtom
1469+
let subRefRecomputes = 0
1470+
const stateAtom = counterRuntime.subscriptionRef((get) =>
1471+
Effect.gen(function*() {
1472+
const chatId = yield* get.some(chatIdAtom)
1473+
subRefRecomputes++
1474+
return yield* SubscriptionRef.make(chatId)
1475+
})
1476+
)
1477+
1478+
const r = Registry.make()
1479+
const unmountDerived = r.mount(derivedStateAtom)
1480+
const unmountSubRef = r.mount(stateAtom)
1481+
1482+
// Initially, chatIdAtom is None, so both should fail with NoSuchElementException
1483+
let derivedResult = r.get(derivedStateAtom)
1484+
let subRefResult = r.get(stateAtom)
1485+
expect(derivedRecomputes).toEqual(0)
1486+
expect(subRefRecomputes).toEqual(0)
1487+
1488+
// Set chatIdAtom to Some("chat-1")
1489+
r.set(chatIdAtom, Option.some("chat-1"))
1490+
await new Promise((resolve) => resolve(null))
1491+
1492+
derivedResult = r.get(derivedStateAtom)
1493+
subRefResult = r.get(stateAtom)
1494+
1495+
assert(Result.isSuccess(derivedResult))
1496+
expect(derivedResult.value).toEqual("chat-1")
1497+
expect(derivedRecomputes).toEqual(1)
1498+
1499+
assert(Result.isSuccess(subRefResult))
1500+
expect(subRefResult.value).toEqual("chat-1")
1501+
expect(subRefRecomputes).toEqual(1)
1502+
1503+
// Change chatIdAtom to Some("chat-2") - both atoms should recompute
1504+
r.set(chatIdAtom, Option.some("chat-2"))
1505+
await new Promise((resolve) => resolve(null))
1506+
1507+
derivedResult = r.get(derivedStateAtom)
1508+
subRefResult = r.get(stateAtom)
1509+
1510+
// The derived atom should have recomputed
1511+
assert(Result.isSuccess(derivedResult))
1512+
expect(derivedResult.value).toEqual("chat-2")
1513+
expect(derivedRecomputes).toEqual(2)
1514+
1515+
// The subscriptionRef atom should ALSO have recomputed
1516+
assert(Result.isSuccess(subRefResult))
1517+
expect(subRefResult.value).toEqual("chat-2")
1518+
expect(subRefRecomputes).toEqual(2)
1519+
1520+
unmountDerived()
1521+
unmountSubRef()
1522+
})
14491523
})
14501524

14511525
interface BuildCounter {

0 commit comments

Comments
 (0)