From bb0da73a364659f4d9e1b9da386c4dbc65ab08a1 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Fri, 26 Jan 2024 09:27:08 +0000 Subject: [PATCH] [ide-react] Preserve scope value update watchers on watcher add (#16729) GitOrigin-RevId: 1168577364fef6e13a58d7d96afdf4ba685fc342 --- .../react-scope-value-store.ts | 4 ++- .../unit/react-scope-value-store.test.ts | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/services/web/frontend/js/features/ide-react/scope-value-store/react-scope-value-store.ts b/services/web/frontend/js/features/ide-react/scope-value-store/react-scope-value-store.ts index 3673af4721..4020119a56 100644 --- a/services/web/frontend/js/features/ide-react/scope-value-store/react-scope-value-store.ts +++ b/services/web/frontend/js/features/ide-react/scope-value-store/react-scope-value-store.ts @@ -273,7 +273,9 @@ export class ReactScopeValueStore implements ScopeValueStore { // useScopeValue, during which the value could change without being // observed if ('value' in item) { - this.scheduleWatcherUpdate(path, item.value, [watcher]) + // add this watcher to any existing watchers scheduled for this path + const { watchers } = this.watcherUpdates.get(path) ?? { watchers: [] } + this.scheduleWatcherUpdate(path, item.value, [...watchers, watcher]) } return () => { diff --git a/services/web/test/frontend/features/ide-react/unit/react-scope-value-store.test.ts b/services/web/test/frontend/features/ide-react/unit/react-scope-value-store.test.ts index 4b3be365ae..7f867ca850 100644 --- a/services/web/test/frontend/features/ide-react/unit/react-scope-value-store.test.ts +++ b/services/web/test/frontend/features/ide-react/unit/react-scope-value-store.test.ts @@ -122,6 +122,35 @@ describe('ReactScopeValueStore', function () { expect(watcher).to.have.been.calledWith('wombat') }) + it('handles multiple watchers for the same path added at the same time before the value is set', async function () { + const store = new ReactScopeValueStore() + + const watcherOne = sinon.stub() + const watcherTwo = sinon.stub() + store.watch('test', watcherOne) + store.watch('test', watcherTwo) + await waitForWatchers(() => { + store.set('test', 'wombat') + }) + + expect(watcherOne).to.have.been.calledWith('wombat') + expect(watcherTwo).to.have.been.calledWith('wombat') + }) + + it('handles multiple watchers for the same path added at the same time after the value is set', async function () { + const store = new ReactScopeValueStore() + store.set('test', 'wombat') + + const watcherOne = sinon.stub() + const watcherTwo = sinon.stub() + store.watch('test', watcherOne) + store.watch('test', watcherTwo) + store.flushUpdates() + + expect(watcherOne).to.have.been.calledWith('wombat') + expect(watcherTwo).to.have.been.calledWith('wombat') + }) + it('throws an error when watching an unknown value', function () { const store = new ReactScopeValueStore() expect(() => store.watch('test', () => {})).to.throw