From 5f9aa3b16965743a6c8e95363b4930bbe40271b1 Mon Sep 17 00:00:00 2001 From: Jay Meistrich Date: Mon, 13 Nov 2023 16:56:13 +0000 Subject: [PATCH] fix set not working before activated load --- src/ObservableObject.ts | 16 ++++++++++----- tests/computedtests.ts | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/ObservableObject.ts b/src/ObservableObject.ts index 2038241a..50d5ed97 100644 --- a/src/ObservableObject.ts +++ b/src/ObservableObject.ts @@ -1064,6 +1064,7 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase( if (onSet) { let allChanges: Change[] = []; let latestValue: any = undefined; + let runNumber = 0; const runChanges = (listenerParams?: ListenerParams) => { // Don't call the set if this is the first value coming in if (allChanges.length > 0) { @@ -1082,8 +1083,15 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase( latestValue = undefined; globalState.pendingNodes.delete(node); + runNumber++; + const thisRunNumber = runNumber; + const attemptNum = { current: 0 }; const run = () => { + if (thisRunNumber !== runNumber) { + // set may get called multiple times before it loads so ignore any previous runs + return; + } let onError: () => void; if (retry) { if (timeoutRetry?.current) { @@ -1093,6 +1101,7 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase( onError = handleError; timeoutRetry = timeout; } + isSetting = true; batch( () => @@ -1118,16 +1127,13 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase( }, ); }; - run(); + whenReady(node.state!.isLoaded, run); } }; const onChangeImmediate = ({ value, changes }: ListenerParams) => { if (!isSetting || isSettingFromSubscribe) { - if ( - get(getNode(node.state!.isLoaded)) && - (changes.length > 1 || !isFunction(changes[0].prevAtPath)) - ) { + if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) { latestValue = value; allChanges.push(...changes); globalState.pendingNodes.set(node, runChanges); diff --git a/tests/computedtests.ts b/tests/computedtests.ts index f39074d9..254d633f 100644 --- a/tests/computedtests.ts +++ b/tests/computedtests.ts @@ -281,6 +281,28 @@ export const run = (isPersist: boolean) => { expect(obs.test.get()).toEqual(true); }); + test('Computed set works before loaded', async () => { + let setValue: number | undefined = undefined; + let getValue: number | undefined = undefined; + const comp = observable( + activated({ + onSet: ({ value }) => { + setValue = value; + }, + get: () => + new Promise((resolve) => + setTimeout(() => { + getValue = 1; + resolve(1); + }, 0), + ), + }), + ); + comp.set(2); + await promiseTimeout(0); + expect(getValue).toEqual(1); + expect(setValue).toEqual(2); + }); test('Computed handler is batched', () => { const obs = observable( activated({ @@ -1233,5 +1255,28 @@ export const run = (isPersist: boolean) => { expect(didSet).toEqual(true); }); + test('get returning twice', async () => { + const num$ = observable(0); + const obs = observable( + activated({ + get: (): Promise | string => + num$.get() > 0 + ? new Promise((resolve) => { + setTimeout(() => { + resolve('hi'); + }, 0); + }) + : '', + }), + ); + const handler = expectChangeHandler(obs); + expect(obs.get()).toEqual(''); + num$.set(1); + await promiseTimeout(0); + expect(obs.get()).toEqual('hi'); + expect(handler).toHaveBeenCalledWith('hi', '', [ + { path: [], pathTypes: [], prevAtPath: '', valueAtPath: 'hi' }, + ]); + }); }); };