diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 223e96d1d9..aa805753f5 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -333,15 +333,18 @@ const buildStore = ( } // a !== atom const aState = readAtomState(pending, a, dirtyAtoms) - if (isSync) { - addDependency(pending, atom, atomState, a, aState) - } else { - const pending = createPending() - addDependency(pending, atom, atomState, a, aState) - mountDependencies(pending, atom, atomState) - flushPending(pending) + try { + return returnAtomValue(aState) + } finally { + if (isSync) { + addDependency(pending, atom, atomState, a, aState!) + } else { + const pending = createPending() + addDependency(pending, atom, atomState, a, aState!) + mountDependencies(pending, atom, atomState) + flushPending(pending) + } } - return returnAtomValue(aState) } let controller: AbortController | undefined let setSelf: ((...args: unknown[]) => unknown) | undefined @@ -493,28 +496,30 @@ const buildStore = ( ...args: As ) => { const aState = getAtomState(a) - let r: R | undefined - if (isSelfAtom(atom, a)) { - if (!hasInitialValue(a)) { - // NOTE technically possible but restricted as it may cause bugs - throw new Error('atom not writable') + try { + if (isSelfAtom(atom, a)) { + if (!hasInitialValue(a)) { + // NOTE technically possible but restricted as it may cause bugs + throw new Error('atom not writable') + } + const hasPrevValue = 'v' in aState + const prevValue = aState.v + const v = args[0] as V + setAtomStateValueOrPromise(a, aState, v) + mountDependencies(pending, a, aState) + if (!hasPrevValue || !Object.is(prevValue, aState.v)) { + addPendingAtom(pending, a, aState) + recomputeDependents(pending, a, aState) + } + return undefined as R + } else { + return writeAtomState(pending, a, ...args) } - const hasPrevValue = 'v' in aState - const prevValue = aState.v - const v = args[0] as V - setAtomStateValueOrPromise(a, aState, v) - mountDependencies(pending, a, aState) - if (!hasPrevValue || !Object.is(prevValue, aState.v)) { - addPendingAtom(pending, a, aState) - recomputeDependents(pending, a, aState) + } finally { + if (!isSync) { + flushPending(pending) } - } else { - r = writeAtomState(pending, a, ...args) as R } - if (!isSync) { - flushPending(pending) - } - return r as R } try { return atomWrite(atom, getter, setter, ...args) @@ -528,9 +533,11 @@ const buildStore = ( ...args: Args ): Result => { const pending = createPending() - const result = writeAtomState(pending, atom, ...args) - flushPending(pending) - return result + try { + return writeAtomState(pending, atom, ...args) + } finally { + flushPending(pending) + } } const mountDependencies = ( @@ -584,11 +591,13 @@ const buildStore = ( let isSync = true try { const onUnmount = atomOnMount(atom, (...args) => { - const result = writeAtomState(pending, atom, ...args) - if (!isSync) { - flushPending(pending) + try { + return writeAtomState(pending, atom, ...args) + } finally { + if (!isSync) { + flushPending(pending) + } } - return result }) if (onUnmount) { mounted.u = () => { diff --git a/tests/vanilla/store.test.tsx b/tests/vanilla/store.test.tsx index 25c02c4d83..859af8730c 100644 --- a/tests/vanilla/store.test.tsx +++ b/tests/vanilla/store.test.tsx @@ -582,6 +582,7 @@ it('should update derived atom even if dependances changed (#2697)', () => { describe('should invoke flushPending only after all atoms are updated (#2804)', () => { const store = createStore() + it('should invoke flushPending only after all atoms are updated with set', () => { const a = atom(0) const setResult = [] @@ -611,6 +612,7 @@ describe('should invoke flushPending only after all atoms are updated (#2804)', 'after store.set', ]) }) + it('should invoke flushPending only after all atoms are updated with mount', () => { const mountResult = [] const a = atom(0) @@ -621,11 +623,6 @@ describe('should invoke flushPending only after all atoms are updated (#2804)', mountResult.push('before onMount setSelf') setSelf(1) mountResult.push('after onMount setSelf') - return () => { - mountResult.push('before onUnmount setSelf') - setSelf(2) - mountResult.push('after onUnmount setSelf') - } } mountResult.push('before store.sub') store.sub(a, () => { @@ -633,20 +630,12 @@ describe('should invoke flushPending only after all atoms are updated (#2804)', }) const unsub = store.sub(m, () => {}) mountResult.push('after store.sub') - mountResult.push('before store.unsub') - unsub() - mountResult.push('after store.unsub') expect(mountResult).not.toEqual([ 'before store.sub', 'before onMount setSelf', 'a value changed - 1', 'after onMount setSelf', 'after store.sub', - 'before store.unsub', - 'before onUnmount setSelf', - 'a value changed - 2', - 'after onUnmount setSelf', - 'after store.unsub', ]) expect(mountResult).toEqual([ 'before store.sub', @@ -654,11 +643,6 @@ describe('should invoke flushPending only after all atoms are updated (#2804)', 'after onMount setSelf', 'a value changed - 1', 'after store.sub', - 'before store.unsub', - 'before onUnmount setSelf', - 'after onUnmount setSelf', - 'a value changed - 2', - 'after store.unsub', ]) }) })