From dd23898af8e8a332dd800eccc11b1ed858f4a5e8 Mon Sep 17 00:00:00 2001 From: huangcheng Date: Wed, 5 Jul 2023 11:29:02 +0800 Subject: [PATCH 01/14] fix: multiple sources updating ignored --- packages/reactive/src/autorun.ts | 15 +++++++++++---- packages/reactive/src/reaction.ts | 2 ++ packages/reactive/src/tracker.ts | 15 ++++++++++----- packages/reactive/src/types.ts | 4 +++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/reactive/src/autorun.ts b/packages/reactive/src/autorun.ts index f2bbd97e4c1..8eaf00fdb94 100644 --- a/packages/reactive/src/autorun.ts +++ b/packages/reactive/src/autorun.ts @@ -19,7 +19,10 @@ interface IValue { export const autorun = (tracker: Reaction, name = 'AutoRun') => { const reaction: Reaction = () => { if (!isFn(tracker)) return - if (reaction._boundary > 0) return + + const updateKey = reaction._boundary.get(reaction._updateTarget) + if (updateKey && updateKey === reaction._updateKey) return + if (ReactionStack.indexOf(reaction) === -1) { releaseBindingReactions(reaction) try { @@ -28,9 +31,11 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { tracker() } finally { ReactionStack.pop() - reaction._boundary++ + if (reaction._updateKey) { + reaction._boundary.set(reaction._updateTarget, reaction._updateKey) + } batchEnd() - reaction._boundary = 0 + reaction._boundary.clear() reaction._memos.cursor = 0 reaction._effects.cursor = 0 } @@ -46,10 +51,12 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { cursor: 0, } } - reaction._boundary = 0 + + reaction._boundary = new Map() reaction._name = name cleanRefs() reaction() + return () => { disposeBindingReactions(reaction) disposeEffects(reaction) diff --git a/packages/reactive/src/reaction.ts b/packages/reactive/src/reaction.ts index 38444eae094..56d70256a1b 100644 --- a/packages/reactive/src/reaction.ts +++ b/packages/reactive/src/reaction.ts @@ -74,6 +74,8 @@ const runReactions = (target: any, key: PropertyKey) => { UntrackCount.value = 0 for (let i = 0, len = reactions.length; i < len; i++) { const reaction = reactions[i] + reaction._updateTarget = target + reaction._updateKey = key if (reaction._isComputed) { reaction._scheduler(reaction) } else if (isScopeBatching()) { diff --git a/packages/reactive/src/tracker.ts b/packages/reactive/src/tracker.ts index f6e2392e666..e5df41871d8 100644 --- a/packages/reactive/src/tracker.ts +++ b/packages/reactive/src/tracker.ts @@ -15,16 +15,19 @@ export class Tracker { name = 'TrackerReaction' ) { this.track._scheduler = (callback) => { - if (this.track._boundary === 0) this.dispose() + if (this.track._boundary.size === 0) this.dispose() if (isFn(callback)) scheduler(callback) } this.track._name = name - this.track._boundary = 0 + this.track._boundary = new Map() } track: Reaction = (tracker: Reaction) => { if (!isFn(tracker)) return this.results - if (this.track._boundary > 0) return + + const updateKey = tracker._boundary.get(tracker._updateTarget) + if (updateKey && updateKey === tracker._updateKey) return + if (ReactionStack.indexOf(this.track) === -1) { releaseBindingReactions(this.track) try { @@ -33,9 +36,11 @@ export class Tracker { this.results = tracker() } finally { ReactionStack.pop() - this.track._boundary++ + if (tracker._updateKey) { + tracker._boundary.set(tracker._updateTarget, tracker._updateKey) + } batchEnd() - this.track._boundary = 0 + this.track._boundary.clear() } } return this.results diff --git a/packages/reactive/src/types.ts b/packages/reactive/src/types.ts index f0fcca41185..c5996f03d4f 100644 --- a/packages/reactive/src/types.ts +++ b/packages/reactive/src/types.ts @@ -61,12 +61,14 @@ export type Dispose = () => void export type Effect = () => void | Dispose export type Reaction = ((...args: any[]) => any) & { - _boundary?: number + _boundary?: Map _name?: string _isComputed?: boolean _dirty?: boolean _context?: any _disposed?: boolean + _updateKey?: string + _updateTarget?: any _property?: PropertyKey _computesSet?: ArraySet _reactionsSet?: ArraySet From 402b1290dfa757b036b3f35281c8bb2bc121360d Mon Sep 17 00:00:00 2001 From: huangcheng Date: Wed, 5 Jul 2023 12:47:00 +0800 Subject: [PATCH 02/14] test: add test case --- .../reactive/src/__tests__/autorun.spec.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index 2e74766b38a..caf77ad1c5e 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -1,6 +1,7 @@ import { observable, reaction, autorun } from '../' import { batch } from '../batch' import { define } from '../model' +import expect from 'expect' const sleep = (duration = 100) => new Promise((resolve) => setTimeout(resolve, duration)) @@ -741,3 +742,51 @@ test('reaction recollect dependencies', () => { expect(fn2).toBeCalledTimes(2) expect(trigger2).toBeCalledTimes(2) }) + +test('multiple source update', () => { + const obs = observable({}) + + const fn1 = jest.fn() + const fn2 = jest.fn() + + autorun(() => { + const A = obs.A + const B = obs.B + if (A !== undefined && B !== undefined) { + obs.C = A / B + fn1() + } + }) + + autorun(() => { + const C = obs.C + const B = obs.B + if (C !== undefined && B !== undefined) { + obs.D = C * B + fn2() + } + }) + + obs.A = 1 + obs.B = 2 + + expect(fn1).toBeCalledTimes(1) + expect(fn2).toBeCalledTimes(1) +}) + +test('same source in nest update', () => { + const obs = observable({}) + + const fn1 = jest.fn() + + autorun(() => { + const B = obs.B + obs.B = 'B' + fn1() + return B + }) + + obs.B = 'B2' + + expect(fn1).toBeCalledTimes(2) +}) From 5a35cb713d9249e77e6c6fe284a637258729931d Mon Sep 17 00:00:00 2001 From: huangcheng Date: Wed, 5 Jul 2023 12:51:17 +0800 Subject: [PATCH 03/14] test: supplementary coverage rate --- packages/reactive/src/__tests__/autorun.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index caf77ad1c5e..5046a430763 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -25,6 +25,12 @@ test('autorun', () => { expect(handler).toBeCalledTimes(2) }) +test('autorun first argument is not a function', () => { + autorun({} as any) + autorun(1 as any) + autorun('1' as any) +}) + test('reaction', () => { const obs = observable({ aa: { From 5fbb7ef738a0baf7108a751109773e2990b33a84 Mon Sep 17 00:00:00 2001 From: huangcheng Date: Wed, 5 Jul 2023 13:06:06 +0800 Subject: [PATCH 04/14] fix: get target and key --- packages/reactive/src/tracker.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/reactive/src/tracker.ts b/packages/reactive/src/tracker.ts index e5df41871d8..df857f59163 100644 --- a/packages/reactive/src/tracker.ts +++ b/packages/reactive/src/tracker.ts @@ -25,8 +25,8 @@ export class Tracker { track: Reaction = (tracker: Reaction) => { if (!isFn(tracker)) return this.results - const updateKey = tracker._boundary.get(tracker._updateTarget) - if (updateKey && updateKey === tracker._updateKey) return + const updateKey = this.track._boundary.get(this.track._updateTarget) + if (updateKey && updateKey === this.track._updateKey) return if (ReactionStack.indexOf(this.track) === -1) { releaseBindingReactions(this.track) @@ -36,8 +36,11 @@ export class Tracker { this.results = tracker() } finally { ReactionStack.pop() - if (tracker._updateKey) { - tracker._boundary.set(tracker._updateTarget, tracker._updateKey) + if (this.track._updateKey) { + this.track._boundary.set( + this.track._updateTarget, + this.track._updateKey + ) } batchEnd() this.track._boundary.clear() From 9963fbf12c34f78a41fd84a03382018ccda5de51 Mon Sep 17 00:00:00 2001 From: huangcheng Date: Wed, 5 Jul 2023 13:12:54 +0800 Subject: [PATCH 05/14] test: add test for tracker --- .../reactive/src/__tests__/tracker.spec.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/reactive/src/__tests__/tracker.spec.ts b/packages/reactive/src/__tests__/tracker.spec.ts index c0da949ea81..e62b6ccf201 100644 --- a/packages/reactive/src/__tests__/tracker.spec.ts +++ b/packages/reactive/src/__tests__/tracker.spec.ts @@ -1,4 +1,5 @@ import { Tracker, observable } from '../' +import expect from 'expect' test('base tracker', () => { const obs = observable({}) @@ -91,3 +92,50 @@ test('shared scheduler with multi tracker(mock react strict mode)', () => { expect(scheduler1).toBeCalledTimes(1) expect(scheduler2).toBeCalledTimes(0) }) + +test('multiple source update', () => { + const obs = observable({}) + + const fn1 = jest.fn() + const fn2 = jest.fn() + + const view1 = () => { + const A = obs.A + const B = obs.B + if (A !== undefined && B !== undefined) { + obs.C = A / B + fn1() + } + } + const scheduler1 = () => { + tracker1.track(view1) + } + + const tracker1 = new Tracker(scheduler1) + + const view2 = () => { + const C = obs.C + const B = obs.B + if (C !== undefined && B !== undefined) { + obs.D = C * B + fn2() + } + } + const scheduler2 = () => { + tracker2.track(view2) + } + + const tracker2 = new Tracker(scheduler2) + + tracker1.track(view1) + tracker2.track(view2) + + obs.A = 1 + obs.B = 2 + + expect(fn1).toBeCalledTimes(1) + expect(fn2).toBeCalledTimes(1) + + tracker1.dispose() + tracker2.dispose() +}) From cf3ea1017f30639e8fd9c6386bb6ad7a0e4572da Mon Sep 17 00:00:00 2001 From: huangcheng Date: Wed, 5 Jul 2023 13:29:48 +0800 Subject: [PATCH 06/14] test: supplementary coverage rate --- packages/reactive/src/__tests__/autorun.spec.ts | 1 - packages/reactive/src/__tests__/tracker.spec.ts | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index 5046a430763..b8c1630defc 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -1,7 +1,6 @@ import { observable, reaction, autorun } from '../' import { batch } from '../batch' import { define } from '../model' -import expect from 'expect' const sleep = (duration = 100) => new Promise((resolve) => setTimeout(resolve, duration)) diff --git a/packages/reactive/src/__tests__/tracker.spec.ts b/packages/reactive/src/__tests__/tracker.spec.ts index e62b6ccf201..a905b328c5d 100644 --- a/packages/reactive/src/__tests__/tracker.spec.ts +++ b/packages/reactive/src/__tests__/tracker.spec.ts @@ -1,5 +1,4 @@ import { Tracker, observable } from '../' -import expect from 'expect' test('base tracker', () => { const obs = observable({}) @@ -19,6 +18,13 @@ test('base tracker', () => { tracker.dispose() }) +test('track argument is not a function', () => { + const scheduler = () => {} + const tracker = new Tracker(scheduler) + + tracker.track({}) +}) + test('nested tracker', () => { const obs = observable({}) const fn = jest.fn() From 79be3d3a3d939c142ede3c426d85e43827891712 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sun, 16 Jul 2023 21:46:20 +0800 Subject: [PATCH 07/14] fix: multiple update keys --- packages/reactive/src/autorun.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/reactive/src/autorun.ts b/packages/reactive/src/autorun.ts index 8eaf00fdb94..abd2cb2c6c9 100644 --- a/packages/reactive/src/autorun.ts +++ b/packages/reactive/src/autorun.ts @@ -21,7 +21,7 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { if (!isFn(tracker)) return const updateKey = reaction._boundary.get(reaction._updateTarget) - if (updateKey && updateKey === reaction._updateKey) return + if (updateKey && updateKey.has(reaction._updateKey)) return if (ReactionStack.indexOf(reaction) === -1) { releaseBindingReactions(reaction) @@ -31,11 +31,21 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { tracker() } finally { ReactionStack.pop() + if (reaction._updateKey) { - reaction._boundary.set(reaction._updateTarget, reaction._updateKey) + const keys = + reaction._boundary.get(reaction._updateTarget) || new Set([]) + keys.add(reaction._updateKey) + reaction._boundary.set(reaction._updateTarget, keys) } + batchEnd() - reaction._boundary.clear() + + const keys = reaction._boundary.get(reaction._updateTarget) + if (keys) { + keys.delete(reaction._updateKey) + } + reaction._memos.cursor = 0 reaction._effects.cursor = 0 } From 37a5046d9a51d1667f8a4e0e1b05e4f821fd9a51 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sun, 16 Jul 2023 21:46:36 +0800 Subject: [PATCH 08/14] test: add test --- packages/reactive/src/__tests__/autorun.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index b8c1630defc..2c5d9ad301d 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -795,3 +795,17 @@ test('same source in nest update', () => { expect(fn1).toBeCalledTimes(2) }) + +test('repeat execute autorun cause by deep indirect dependency', () => { + const obs: any = observable({ aa: 1, bb: 1, cc: 1 }) + const fn = jest.fn() + const fn2 = jest.fn() + const fn3 = jest.fn() + autorun(() => fn((obs.aa = obs.bb + obs.cc))) + autorun(() => fn2((obs.bb = obs.aa + obs.cc))) + autorun(() => fn3((obs.cc = obs.aa + obs.bb))) + + expect(fn).toBeCalledTimes(4) + expect(fn2).toBeCalledTimes(4) + expect(fn3).toBeCalledTimes(3) +}) From 438c5c64716111ff7dc61969fde793c828746f39 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sun, 16 Jul 2023 21:48:05 +0800 Subject: [PATCH 09/14] chore: types --- packages/reactive/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactive/src/types.ts b/packages/reactive/src/types.ts index c5996f03d4f..16d5b9797af 100644 --- a/packages/reactive/src/types.ts +++ b/packages/reactive/src/types.ts @@ -61,7 +61,7 @@ export type Dispose = () => void export type Effect = () => void | Dispose export type Reaction = ((...args: any[]) => any) & { - _boundary?: Map + _boundary?: Map> _name?: string _isComputed?: boolean _dirty?: boolean From edc6affca6f8ea0dbee34afe452de16c66fd9f12 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sun, 16 Jul 2023 21:54:32 +0800 Subject: [PATCH 10/14] fix: tracker multiple update keys --- packages/reactive/src/tracker.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/reactive/src/tracker.ts b/packages/reactive/src/tracker.ts index df857f59163..9223ce62a5e 100644 --- a/packages/reactive/src/tracker.ts +++ b/packages/reactive/src/tracker.ts @@ -26,7 +26,7 @@ export class Tracker { if (!isFn(tracker)) return this.results const updateKey = this.track._boundary.get(this.track._updateTarget) - if (updateKey && updateKey === this.track._updateKey) return + if (updateKey && updateKey.has(this.track._updateKey)) return if (ReactionStack.indexOf(this.track) === -1) { releaseBindingReactions(this.track) @@ -36,14 +36,20 @@ export class Tracker { this.results = tracker() } finally { ReactionStack.pop() + if (this.track._updateKey) { - this.track._boundary.set( - this.track._updateTarget, - this.track._updateKey - ) + const keys = + this.track._boundary.get(this.track._updateTarget) || new Set([]) + keys.add(this.track._updateKey) + this.track._boundary.set(this.track._updateTarget, keys) } + batchEnd() - this.track._boundary.clear() + + const keys = this.track._boundary.get(this.track._updateTarget) + if (keys) { + keys.delete(this.track._updateKey) + } } } return this.results From 2d53fcacdcc8249e88db00f90d673254003c5387 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sun, 16 Jul 2023 22:14:31 +0800 Subject: [PATCH 11/14] test: add test --- .../reactive/src/__tests__/tracker.spec.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/reactive/src/__tests__/tracker.spec.ts b/packages/reactive/src/__tests__/tracker.spec.ts index a905b328c5d..03910ef3156 100644 --- a/packages/reactive/src/__tests__/tracker.spec.ts +++ b/packages/reactive/src/__tests__/tracker.spec.ts @@ -145,3 +145,50 @@ test('multiple source update', () => { tracker1.dispose() tracker2.dispose() }) + +test('repeat execute autorun cause by deep indirect dependency', () => { + const obs: any = observable({ aa: 1, bb: 1, cc: 1 }) + + const fn1 = jest.fn() + const fn2 = jest.fn() + const fn3 = jest.fn() + + const view1 = () => { + fn1((obs.aa = obs.bb + obs.cc)) + } + const scheduler1 = () => { + tracker1.track(view1) + } + + const tracker1 = new Tracker(scheduler1) + + const view2 = () => { + fn2((obs.bb = obs.aa + obs.cc)) + } + const scheduler2 = () => { + tracker2.track(view2) + } + + const tracker2 = new Tracker(scheduler2) + + const view3 = () => { + fn3((obs.cc = obs.aa + obs.bb)) + } + const scheduler3 = () => { + tracker3.track(view3) + } + + const tracker3 = new Tracker(scheduler3) + + tracker1.track(view1) + tracker2.track(view2) + tracker3.track(view3) + + expect(fn1).toBeCalledTimes(4) + expect(fn2).toBeCalledTimes(4) + expect(fn3).toBeCalledTimes(3) + + tracker1.dispose() + tracker2.dispose() + tracker3.dispose() +}) From 914c012c896cf3b1dc8285690688fb288596562d Mon Sep 17 00:00:00 2001 From: huangcheng Date: Tue, 18 Jul 2023 13:29:29 +0800 Subject: [PATCH 12/14] fix: delete key incorrect --- .../reactive/src/__tests__/autorun.spec.ts | 28 +++++++++ .../reactive/src/__tests__/tracker.spec.ts | 61 ++++++++++++++++++- packages/reactive/src/autorun.ts | 7 ++- packages/reactive/src/tracker.ts | 7 ++- 4 files changed, 95 insertions(+), 8 deletions(-) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index 2c5d9ad301d..1b90e8d1b9e 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -809,3 +809,31 @@ test('repeat execute autorun cause by deep indirect dependency', () => { expect(fn2).toBeCalledTimes(4) expect(fn3).toBeCalledTimes(3) }) + +test('batch execute autorun cause by deep indirect dependency', () => { + const obs: any = observable({ aa: 1, bb: 1, cc: 1 }) + const fn = jest.fn() + const fn2 = jest.fn() + const fn3 = jest.fn() + autorun(() => fn((obs.aa = obs.bb + obs.cc))) + autorun(() => fn2((obs.bb = obs.aa + obs.cc))) + autorun(() => fn3((obs.cc = obs.aa + obs.bb))) + + expect(fn).toBeCalledTimes(4) + expect(fn2).toBeCalledTimes(4) + expect(fn3).toBeCalledTimes(3) + + fn.mockClear() + fn2.mockClear() + fn3.mockClear() + + batch(() => { + obs.aa = 100 + obs.bb = 100 + obs.cc = 100 + }) + + expect(fn).toBeCalledTimes(2) + expect(fn2).toBeCalledTimes(2) + expect(fn3).toBeCalledTimes(2) +}) diff --git a/packages/reactive/src/__tests__/tracker.spec.ts b/packages/reactive/src/__tests__/tracker.spec.ts index 03910ef3156..ad4d02363f6 100644 --- a/packages/reactive/src/__tests__/tracker.spec.ts +++ b/packages/reactive/src/__tests__/tracker.spec.ts @@ -1,4 +1,4 @@ -import { Tracker, observable } from '../' +import { Tracker, observable, batch } from '../' test('base tracker', () => { const obs = observable({}) @@ -146,7 +146,7 @@ test('multiple source update', () => { tracker2.dispose() }) -test('repeat execute autorun cause by deep indirect dependency', () => { +test('repeat execute tracker cause by deep indirect dependency', () => { const obs: any = observable({ aa: 1, bb: 1, cc: 1 }) const fn1 = jest.fn() @@ -192,3 +192,60 @@ test('repeat execute autorun cause by deep indirect dependency', () => { tracker2.dispose() tracker3.dispose() }) + +test('batch execute tracker cause by deep indirect dependency', () => { + const obs: any = observable({ aa: 1, bb: 1, cc: 1 }) + + const fn1 = jest.fn() + const fn2 = jest.fn() + const fn3 = jest.fn() + + const view1 = () => { + fn1((obs.aa = obs.bb + obs.cc)) + } + const scheduler1 = () => { + tracker1.track(view1) + } + + const tracker1 = new Tracker(scheduler1) + + const view2 = () => { + fn2((obs.bb = obs.aa + obs.cc)) + } + const scheduler2 = () => { + tracker2.track(view2) + } + + const tracker2 = new Tracker(scheduler2) + + const view3 = () => { + fn3((obs.cc = obs.aa + obs.bb)) + } + const scheduler3 = () => { + tracker3.track(view3) + } + + const tracker3 = new Tracker(scheduler3) + + tracker1.track(view1) + tracker2.track(view2) + tracker3.track(view3) + + expect(fn1).toBeCalledTimes(4) + expect(fn2).toBeCalledTimes(4) + expect(fn3).toBeCalledTimes(3) + + fn1.mockClear() + fn2.mockClear() + fn3.mockClear() + + batch(() => { + obs.aa = 100 + obs.bb = 100 + obs.cc = 100 + }) + + expect(fn1).toBeCalledTimes(2) + expect(fn2).toBeCalledTimes(2) + expect(fn3).toBeCalledTimes(2) +}) diff --git a/packages/reactive/src/autorun.ts b/packages/reactive/src/autorun.ts index abd2cb2c6c9..eb02aab34b3 100644 --- a/packages/reactive/src/autorun.ts +++ b/packages/reactive/src/autorun.ts @@ -32,10 +32,11 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { } finally { ReactionStack.pop() - if (reaction._updateKey) { + const key = reaction._updateKey + if (key) { const keys = reaction._boundary.get(reaction._updateTarget) || new Set([]) - keys.add(reaction._updateKey) + keys.add(key) reaction._boundary.set(reaction._updateTarget, keys) } @@ -43,7 +44,7 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { const keys = reaction._boundary.get(reaction._updateTarget) if (keys) { - keys.delete(reaction._updateKey) + keys.delete(key) } reaction._memos.cursor = 0 diff --git a/packages/reactive/src/tracker.ts b/packages/reactive/src/tracker.ts index 9223ce62a5e..c710ce8262c 100644 --- a/packages/reactive/src/tracker.ts +++ b/packages/reactive/src/tracker.ts @@ -37,10 +37,11 @@ export class Tracker { } finally { ReactionStack.pop() - if (this.track._updateKey) { + const key = this.track._updateKey + if (key) { const keys = this.track._boundary.get(this.track._updateTarget) || new Set([]) - keys.add(this.track._updateKey) + keys.add(key) this.track._boundary.set(this.track._updateTarget, keys) } @@ -48,7 +49,7 @@ export class Tracker { const keys = this.track._boundary.get(this.track._updateTarget) if (keys) { - keys.delete(this.track._updateKey) + keys.delete(key) } } } From 4c7a748c37f443e86acee558073a64d10036f6e9 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sat, 22 Jul 2023 23:00:11 +0800 Subject: [PATCH 13/14] test: add test --- .../reactive/src/__tests__/autorun.spec.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index 1b90e8d1b9e..f1aaef9cf2d 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -837,3 +837,23 @@ test('batch execute autorun cause by deep indirect dependency', () => { expect(fn2).toBeCalledTimes(2) expect(fn3).toBeCalledTimes(2) }) + +test('multiple update should trigger only one', () => { + const obs = observable({ aa: 1, bb: 1 }) + + autorun(() => { + obs.aa = obs.bb + 1 + obs.bb = obs.aa + 1 + }) + + expect(obs.aa).toBe(2) + expect(obs.bb).toBe(3) + + autorun(() => { + obs.aa = obs.bb + 1 + obs.bb = obs.aa + 1 + }) + + expect(obs.aa).toBe(6) + expect(obs.bb).toBe(7) +}) From 7f859a3f6cd9f2903efc0ea51996f6e2f24169c6 Mon Sep 17 00:00:00 2001 From: huangcheng <1530844743@qq.com> Date: Sun, 23 Jul 2023 15:51:21 +0800 Subject: [PATCH 14/14] fix: delete target incorrect --- docs/index.md | 133 +------------------------------ docs/index.tsx | 98 +++++++++++++++++++++++ packages/reactive/src/autorun.ts | 8 +- packages/reactive/src/tracker.ts | 8 +- 4 files changed, 107 insertions(+), 140 deletions(-) create mode 100644 docs/index.tsx diff --git a/docs/index.md b/docs/index.md index 9e2db9bc1a8..646580a3f6c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,132 +1 @@ ---- -title: Formily - Alibaba unified front-end form solution -order: 10 -hero: - title: Alibaba Formily - desc: Alibaba Unified Front-end Form Solution - actions: - - text: Introduction - link: /guide - - text: Quick start - link: /guide/quick-start -features: - - icon: https://img.alicdn.com/imgextra/i2/O1CN016i72sH1c5wh1kyy9U_!!6000000003550-55-tps-800-800.svg - title: Easier to Use - desc: Out of the box, rich cases - - icon: https://img.alicdn.com/imgextra/i1/O1CN01bHdrZJ1rEOESvXEi5_!!6000000005599-55-tps-800-800.svg - title: More Efficient - desc: Fool writing, ultra-high performance - - icon: https://img.alicdn.com/imgextra/i3/O1CN01xlETZk1G0WSQT6Xii_!!6000000000560-55-tps-800-800.svg - title: More Professional - desc: Complete, flexible and elegant -footer: Open-source MIT Licensed | Copyright © 2019-present
Powered by self ---- - -```tsx -/** - * inline: true - */ -import React from 'react' -import { Section } from './site/Section' -import './site/styles.less' - -export default () => ( -
- -
-) -``` - -```tsx -/** - * inline: true - */ -import React from 'react' -import { Section } from './site/Section' -import './site/styles.less' - -export default () => ( -
- - - -
-) -``` - -```tsx -/** - * inline: true - */ -import React from 'react' -import { Section } from './site/Section' -import './site/styles.less' - -export default () => ( -
- - - -
-) -``` - -```tsx -/** - * inline: true - */ -import React from 'react' -import { Section } from './site/Section' -import { Contributors } from './site/Contributors' -import './site/styles.less' - -export default () => ( -
- -
-) -``` - -```tsx -/** - * inline: true - */ -import React from 'react' -import { Section } from './site/Section' -import { QrCode, QrCodeGroup } from './site/QrCode' -import './site/styles.less' - -export default () => ( -
- - - -
-) -``` + diff --git a/docs/index.tsx b/docs/index.tsx new file mode 100644 index 00000000000..22e56f15428 --- /dev/null +++ b/docs/index.tsx @@ -0,0 +1,98 @@ +import React from 'react' + +import { autorun, batch, reaction, observable } from '@formily/reactive' + +const fieldA = observable({ + value: '', + visible: true, +}) + +const fieldB = observable({ + value: '', + visible: true, + cache: '', +}) + +const fieldC = observable({ + value: '', + visible: true, + cache: '', +}) + +// ===== fieldB reaction ===== +reaction( + () => fieldB.value, + () => { + if (fieldB.value && fieldB.visible === false) { + fieldB.cache = fieldB.value + // 删除 fieldB.value 时,会重新 runReaction + delete fieldB.value + } + } +) + +reaction( + () => fieldB.visible, + () => { + if (fieldB.visible === true) { + // debugger + console.log('fieldB.cache: ', fieldB.cache) + // 执行到这里时,不会执行 fieldB.value 的 autorun,因为在上面 delete fieldB.value 时,已经执行了 + fieldB.value = fieldB.cache + } + } +) + +// ===== fieldC reaction ===== +reaction( + () => fieldC.value, + () => { + if (fieldC.value && fieldC.visible === false) { + fieldC.cache = fieldC.value + delete fieldC.value + } + } +) + +reaction( + () => fieldC.visible, + () => { + if (fieldC.visible === true) { + fieldC.value = fieldC.cache + } + } +) + +// ===== schema 渲染 ===== +autorun(() => { + fieldB.visible = fieldA.value === 'fieldA' +}, 'A') + +autorun(() => { + fieldC.visible = fieldB.value === 'fieldB' +}, 'B') + +// fieldB.value = 'fieldB' +// fieldC.value = 'fieldC' +// fieldA.value = 'fieldA' + +batch(() => { + fieldB.value = 'fieldB' + fieldC.value = 'fieldC' + // debugger + fieldA.value = 'fieldA' + // window.xxx = true +}) + +console.log( + 'fieldA.visible:', + fieldA.visible, + 'fieldB.visible:', + fieldB.visible, + 'fieldC.visible:', + fieldC.visible +) + +const App = () =>
123123
+ +export default App diff --git a/packages/reactive/src/autorun.ts b/packages/reactive/src/autorun.ts index eb02aab34b3..adc63113880 100644 --- a/packages/reactive/src/autorun.ts +++ b/packages/reactive/src/autorun.ts @@ -33,16 +33,16 @@ export const autorun = (tracker: Reaction, name = 'AutoRun') => { ReactionStack.pop() const key = reaction._updateKey + const target = reaction._updateTarget if (key) { - const keys = - reaction._boundary.get(reaction._updateTarget) || new Set([]) + const keys = reaction._boundary.get(target) || new Set([]) keys.add(key) - reaction._boundary.set(reaction._updateTarget, keys) + reaction._boundary.set(target, keys) } batchEnd() - const keys = reaction._boundary.get(reaction._updateTarget) + const keys = reaction._boundary.get(target) if (keys) { keys.delete(key) } diff --git a/packages/reactive/src/tracker.ts b/packages/reactive/src/tracker.ts index c710ce8262c..27ffc9b2f5b 100644 --- a/packages/reactive/src/tracker.ts +++ b/packages/reactive/src/tracker.ts @@ -38,16 +38,16 @@ export class Tracker { ReactionStack.pop() const key = this.track._updateKey + const target = this.track._updateTarget if (key) { - const keys = - this.track._boundary.get(this.track._updateTarget) || new Set([]) + const keys = this.track._boundary.get(target) || new Set([]) keys.add(key) - this.track._boundary.set(this.track._updateTarget, keys) + this.track._boundary.set(target, keys) } batchEnd() - const keys = this.track._boundary.get(this.track._updateTarget) + const keys = this.track._boundary.get(target) if (keys) { keys.delete(key) }