diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts b/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts index bf3c37f39b..9e72b5e245 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts @@ -144,7 +144,6 @@ describe('injectMutationState', () => { {{ mutation.status }} } `, - standalone: true, }) class FakeComponent { name = input.required() diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts b/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts index 3ea18743d7..2ecccf882e 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts @@ -58,8 +58,6 @@ describe('injectMutation', () => { })) }) - TestBed.tick() - mutation.mutate(result) vi.advanceTimersByTime(1) @@ -133,9 +131,17 @@ describe('injectMutation', () => { mutation.mutate('xyz') - const mutations = mutationCache.find({ mutationKey: ['2'] }) + mutationKey.set(['3']) + + mutation.mutate('xyz') - expect(mutations?.options.mutationKey).toEqual(['2']) + expect(mutationCache.find({ mutationKey: ['1'] })).toBeUndefined() + expect( + mutationCache.find({ mutationKey: ['2'] })?.options.mutationKey, + ).toEqual(['2']) + expect( + mutationCache.find({ mutationKey: ['3'] })?.options.mutationKey, + ).toEqual(['3']) }) test('should reset state after invoking mutation.reset', async () => { @@ -310,7 +316,6 @@ describe('injectMutation', () => { {{ mutation.data() }} `, - standalone: true, }) class FakeComponent { name = input.required() @@ -333,8 +338,6 @@ describe('injectMutation', () => { button.triggerEventHandler('click') await resolveMutations() - fixture.detectChanges() - const text = debugElement.query(By.css('span')).nativeElement.textContent expect(text).toEqual('value') const mutation = mutationCache.find({ mutationKey: ['fake', 'value'] }) @@ -351,7 +354,6 @@ describe('injectMutation', () => { {{ mutation.data() }} `, - standalone: true, }) class FakeComponent { name = input.required() @@ -394,6 +396,37 @@ describe('injectMutation', () => { expect(mutation2!.options.mutationKey).toEqual(['fake', 'updatedValue']) }) + test('should have pending state when mutating in constructor', async () => { + @Component({ + selector: 'app-fake', + template: ` + {{ mutation.isPending() ? 'pending' : 'not pending' }} + `, + }) + class FakeComponent { + mutation = injectMutation(() => ({ + mutationKey: ['fake'], + mutationFn: () => sleep(0).then(() => 'fake'), + })) + + constructor() { + this.mutation.mutate() + } + } + + const fixture = TestBed.createComponent(FakeComponent) + const { debugElement } = fixture + const span = debugElement.query(By.css('span')) + + vi.advanceTimersByTime(1) + + expect(span.nativeElement.textContent).toEqual('pending') + + await resolveMutations() + + expect(span.nativeElement.textContent).toEqual('not pending') + }) + describe('throwOnError', () => { test('should evaluate throwOnError when mutation is expected to throw', async () => { const err = new Error('Expected mock error. All is well!') diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts index 44964b7927..0309296430 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts @@ -520,7 +520,6 @@ describe('injectQuery', () => { @Component({ selector: 'app-fake', template: `{{ query.data() }}`, - standalone: true, }) class FakeComponent { name = input.required() diff --git a/packages/angular-query-experimental/src/inject-mutation.ts b/packages/angular-query-experimental/src/inject-mutation.ts index 16b22ed2f7..ad8fadc015 100644 --- a/packages/angular-query-experimental/src/inject-mutation.ts +++ b/packages/angular-query-experimental/src/inject-mutation.ts @@ -4,7 +4,6 @@ import { NgZone, assertInInjectionContext, computed, - effect, inject, signal, untracked, @@ -71,7 +70,15 @@ export function injectMutation< null return computed(() => { - return (instance ||= new MutationObserver(queryClient, optionsSignal())) + const observerOptions = optionsSignal() + return untracked(() => { + if (instance) { + instance.setOptions(observerOptions) + } else { + instance = new MutationObserver(queryClient, observerOptions) + } + return instance + }) }) })() @@ -84,14 +91,6 @@ export function injectMutation< } }) - /** - * Computed signal that gets result from mutation cache based on passed options - */ - const resultFromInitialOptionsSignal = computed(() => { - const observer = observerSignal() - return observer.getCurrentResult() - }) - /** * Signal that contains result set by subscriber */ @@ -102,50 +101,36 @@ export function injectMutation< TContext > | null>(null) - effect( - () => { - const observer = observerSignal() - const observerOptions = optionsSignal() - - untracked(() => { - observer.setOptions(observerOptions) - }) - }, - { - injector, - }, - ) - - effect( - () => { - // observer.trackResult is not used as this optimization is not needed for Angular - const observer = observerSignal() + /** + * Computed signal that gets result from mutation cache based on passed options + */ + const resultFromInitialOptionsSignal = computed(() => { + const observer = observerSignal() - untracked(() => { - const unsubscribe = ngZone.runOutsideAngular(() => - observer.subscribe( - notifyManager.batchCalls((state) => { - ngZone.run(() => { - if ( - state.isError && - shouldThrowError(observer.options.throwOnError, [state.error]) - ) { - ngZone.onError.emit(state.error) - throw state.error - } + untracked(() => { + const unsubscribe = ngZone.runOutsideAngular(() => + // observer.trackResult is not used as this optimization is not needed for Angular + observer.subscribe( + notifyManager.batchCalls((state) => { + ngZone.run(() => { + if ( + state.isError && + shouldThrowError(observer.options.throwOnError, [state.error]) + ) { + ngZone.onError.emit(state.error) + throw state.error + } + + resultFromSubscriberSignal.set(state) + }) + }), + ), + ) + destroyRef.onDestroy(unsubscribe) + }) - resultFromSubscriberSignal.set(state) - }) - }), - ), - ) - destroyRef.onDestroy(unsubscribe) - }) - }, - { - injector, - }, - ) + return observer.getCurrentResult() + }) const resultSignal = computed(() => { const resultFromSubscriber = resultFromSubscriberSignal()