Skip to content

Commit 28a45f6

Browse files
committed
add single recomputeDependents to batch
1 parent 023480c commit 28a45f6

File tree

2 files changed

+34
-27
lines changed

2 files changed

+34
-27
lines changed

src/vanilla/store.ts

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,11 @@ type BatchPriority = 0 | 1 | 2
173173

174174
type Batch = [
175175
/** high priority listeners */
176-
priority0: Set<() => void>,
176+
priority0: Set<(batch: Batch) => void>,
177177
/** atom listeners */
178-
priority1: Set<() => void>,
178+
priority1: Set<(batch: Batch) => void>,
179179
/** atom mount hooks */
180-
priority2: Set<() => void>,
180+
priority2: Set<(batch: Batch) => void>,
181181
] & {
182182
/** changed Atoms */
183183
C: Set<AnyAtom>
@@ -189,7 +189,7 @@ const createBatch = (): Batch =>
189189
const addBatchFunc = (
190190
batch: Batch,
191191
priority: BatchPriority,
192-
fn: () => void,
192+
fn: (batch: Batch) => void,
193193
) => {
194194
batch[priority].add(fn)
195195
}
@@ -203,21 +203,20 @@ const registerBatchAtom = (
203203
batch.C.add(atom)
204204
atomState.u?.(batch)
205205
const scheduleListeners = () => {
206-
atomState.m?.l.forEach((listener) => addBatchFunc(batch, 1, listener))
206+
atomState.m?.l.forEach((listener) =>
207+
addBatchFunc(batch, 1, () => listener()),
208+
)
207209
}
208210
addBatchFunc(batch, 1, scheduleListeners)
209211
}
210212
}
211213

212-
const flushBatch = (
213-
batch: Batch,
214-
recomputeDependents: (batch: Batch) => void,
215-
) => {
214+
const flushBatch = (batch: Batch) => {
216215
let error: AnyError
217216
let hasError = false
218-
const call = (fn: () => void) => {
217+
const call = (fn: (batch: Batch) => void) => {
219218
try {
220-
fn()
219+
fn(batch)
221220
} catch (e) {
222221
if (!hasError) {
223222
error = e
@@ -226,8 +225,6 @@ const flushBatch = (
226225
}
227226
}
228227
while (batch.C.size || batch.some((channel) => channel.size)) {
229-
recomputeDependents(batch)
230-
batch.C.clear()
231228
for (const channel of batch) {
232229
channel.forEach(call)
233230
channel.clear()
@@ -380,7 +377,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
380377
const batch = createBatch()
381378
addDependency(atom, atomState, a, aState)
382379
mountDependencies(batch, atom, atomState)
383-
recomputeAndFlushBatch(batch)
380+
flushBatch(batch)
384381
}
385382
}
386383
}
@@ -422,7 +419,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
422419
if (atomState.m) {
423420
const batch = createBatch()
424421
mountDependencies(batch, atom, atomState)
425-
recomputeAndFlushBatch(batch)
422+
flushBatch(batch)
426423
}
427424
}
428425
valueOrPromise.then(complete, complete)
@@ -539,11 +536,9 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
539536
}
540537
delete aState.x
541538
}
539+
batch.C.clear()
542540
}
543541

544-
const recomputeAndFlushBatch = (batch: Batch) =>
545-
flushBatch(batch, recomputeDependents)
546-
547542
const writeAtomState = <Value, Args extends unknown[], Result>(
548543
batch: Batch,
549544
atom: WritableAtom<Value, Args, Result>,
@@ -569,6 +564,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
569564
mountDependencies(batch, a, aState)
570565
if (prevEpochNumber !== aState.n) {
571566
dirtyDependents(aState)
567+
addBatchFunc(batch, 0, recomputeDependents)
572568
registerBatchAtom(batch, a, aState)
573569
}
574570
return undefined as R
@@ -577,7 +573,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
577573
}
578574
} finally {
579575
if (!isSync) {
580-
recomputeAndFlushBatch(batch)
576+
flushBatch(batch)
581577
}
582578
}
583579
}
@@ -596,7 +592,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
596592
try {
597593
return writeAtomState(batch, atom, ...args)
598594
} finally {
599-
recomputeAndFlushBatch(batch)
595+
flushBatch(batch)
600596
}
601597
}
602598

@@ -653,7 +649,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
653649
return writeAtomState(batch, atom, ...args)
654650
} finally {
655651
if (!isSync) {
656-
recomputeAndFlushBatch(batch)
652+
flushBatch(batch)
657653
}
658654
}
659655
}
@@ -690,7 +686,7 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
690686
// unmount self
691687
const onUnmount = atomState.m.u
692688
if (onUnmount) {
693-
addBatchFunc(batch, 2, () => onUnmount(batch))
689+
addBatchFunc(batch, 2, onUnmount)
694690
}
695691
delete atomState.m
696692
atomState.h?.(batch)
@@ -710,12 +706,12 @@ const buildStore = (...storeArgs: StoreArgs): Store => {
710706
const mounted = mountAtom(batch, atom, atomState)
711707
const listeners = mounted.l
712708
listeners.add(listener)
713-
recomputeAndFlushBatch(batch)
709+
flushBatch(batch)
714710
return () => {
715711
listeners.delete(listener)
716712
const batch = createBatch()
717713
unmountAtom(batch, atom, atomState)
718-
recomputeAndFlushBatch(batch)
714+
flushBatch(batch)
719715
}
720716
}
721717

tests/vanilla/effect.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { expect, it, vi } from 'vitest'
2-
import type { Atom, Getter, Setter } from 'jotai/vanilla'
2+
import type { Atom, Getter, PrimitiveAtom, Setter } from 'jotai/vanilla'
33
import { atom, createStore } from 'jotai/vanilla'
44

55
type Store = ReturnType<typeof createStore>
66
type GetAtomState = Parameters<Parameters<Store['unstable_derive']>[0]>[0]
77
type AtomState = NonNullable<ReturnType<GetAtomState>>
8+
type Batch = Parameters<NonNullable<AtomState['u']>>[0]
89
type AnyAtom = Atom<unknown>
910

1011
type Cleanup = () => void
@@ -59,7 +60,7 @@ function syncEffect(effect: Effect): Atom<void> {
5960
store.set(refreshAtom, (v) => v + 1)
6061
} else {
6162
// unmount
62-
batch[0].add(() => {
63+
scheduleListener(batch, () => {
6364
ref.cleanup?.()
6465
delete ref.cleanup
6566
})
@@ -69,7 +70,17 @@ function syncEffect(effect: Effect): Atom<void> {
6970
internalAtomState.u = (batch) => {
7071
originalUpdateHook?.(batch)
7172
// update
72-
batch[0].add(runEffect)
73+
scheduleListener(batch, runEffect)
74+
}
75+
function scheduleListener(batch: Batch, listener: () => void) {
76+
if (batch[0].size === 0) {
77+
store.set(
78+
atom(0, function (this: PrimitiveAtom<number>, _, set) {
79+
set(this, 1)
80+
}),
81+
)
82+
}
83+
batch[0].add(listener)
7384
}
7485
}
7586
return atom((get) => {

0 commit comments

Comments
 (0)