EffectFlags,
type Subscriber,
activeSub,
+ batch,
refreshComputed,
} from './effect'
import type { Ref } from './ref'
/**
* @internal
*/
- notify(): void {
+ notify(): true | void {
this.flags |= EffectFlags.DIRTY
- // avoid infinite self recursion
- if (activeSub !== this) {
- this.dep.notify()
+ if (
+ !(this.flags & EffectFlags.NOTIFIED) &&
+ // avoid infinite self recursion
+ activeSub !== this
+ ) {
+ batch(this)
+ return true
} else if (__DEV__) {
// TODO warn
}
// original order at the end of the batch, but onTrigger hooks should
// be invoked in original order here.
for (let head = this.subsHead; head; head = head.nextSub) {
- if (
- __DEV__ &&
- head.sub.onTrigger &&
- !(head.sub.flags & EffectFlags.NOTIFIED)
- ) {
+ if (head.sub.onTrigger && !(head.sub.flags & EffectFlags.NOTIFIED)) {
head.sub.onTrigger(
extend(
{
}
}
for (let link = this.subs; link; link = link.prevSub) {
- link.sub.notify()
+ if (link.sub.notify()) {
+ // if notify() returns `true`, this is a computed. Also call notify
+ // on its dep - it's called here instead of inside computed's notify
+ // in order to reduce call stack depth.
+ ;(link.sub as ComputedRefImpl).dep.notify()
+ }
}
} finally {
endBatch()
export let activeSub: Subscriber | undefined
export enum EffectFlags {
+ /**
+ * ReactiveEffect only
+ */
ACTIVE = 1 << 0,
RUNNING = 1 << 1,
TRACKING = 1 << 2,
/**
* @internal
*/
- notify(): void
+ next?: Subscriber
+ /**
+ * returning `true` indicates it's a computed that needs to call notify
+ * on its dep too
+ * @internal
+ */
+ notify(): true | void
}
const pausedQueueEffects = new WeakSet<ReactiveEffect>()
/**
* @internal
*/
- nextEffect?: ReactiveEffect = undefined
+ next?: Subscriber = undefined
/**
* @internal
*/
return
}
if (!(this.flags & EffectFlags.NOTIFIED)) {
- this.flags |= EffectFlags.NOTIFIED
- this.nextEffect = batchedEffect
- batchedEffect = this
+ batch(this)
}
}
// }
let batchDepth = 0
-let batchedEffect: ReactiveEffect | undefined
+let batchedSub: Subscriber | undefined
+
+export function batch(sub: Subscriber): void {
+ sub.flags |= EffectFlags.NOTIFIED
+ sub.next = batchedSub
+ batchedSub = sub
+}
/**
* @internal
}
let error: unknown
- while (batchedEffect) {
- let e: ReactiveEffect | undefined = batchedEffect
- batchedEffect = undefined
+ while (batchedSub) {
+ let e: Subscriber | undefined = batchedSub
+ batchedSub = undefined
while (e) {
- const next: ReactiveEffect | undefined = e.nextEffect
- e.nextEffect = undefined
+ const next: Subscriber | undefined = e.next
+ e.next = undefined
e.flags &= ~EffectFlags.NOTIFIED
if (e.flags & EffectFlags.ACTIVE) {
try {
- e.trigger()
+ // ACTIVE flag is effect-only
+ ;(e as ReactiveEffect).trigger()
} catch (err) {
if (!error) error = err
}