Skip to content

Commit f3e109c

Browse files
committed
Fix isRestoring() handling
1 parent dfa8c31 commit f3e109c

File tree

4 files changed

+73
-37
lines changed

4 files changed

+73
-37
lines changed

examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,25 @@ export class ExampleComponent {
3030
}))
3131

3232
readonly nextButtonDisabled = computed(
33-
() => !this.#hasNextPage() || this.#isFetchingNextPage(),
33+
() => !this.query.hasNextPage() || this.query.isFetchingNextPage(),
3434
)
3535

3636
readonly nextButtonText = computed(() =>
37-
this.#isFetchingNextPage()
37+
this.query.isFetchingNextPage()
3838
? 'Loading more...'
39-
: this.#hasNextPage()
39+
: this.query.hasNextPage()
4040
? 'Load newer'
4141
: 'Nothing more to load',
4242
)
4343

4444
readonly previousButtonDisabled = computed(
45-
() => !this.#hasPreviousPage() || this.#isFetchingNextPage(),
45+
() => !this.query.hasPreviousPage() || this.query.isFetchingNextPage(),
4646
)
4747
readonly previousButtonText = computed(() =>
48-
this.#isFetchingPreviousPage()
48+
this.query.isFetchingPreviousPage()
4949
? 'Loading more...'
50-
: this.#hasPreviousPage()
50+
: this.query.hasPreviousPage()
5151
? 'Load Older'
5252
: 'Nothing more to load',
5353
)
54-
55-
readonly #hasPreviousPage = this.query.hasPreviousPage
56-
readonly #hasNextPage = this.query.hasNextPage
57-
readonly #isFetchingPreviousPage = this.query.isFetchingPreviousPage
58-
readonly #isFetchingNextPage = this.query.isFetchingNextPage
5954
}

examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { TasksService } from '../services/tasks.service'
3636
<input type="text" [(ngModel)]="newItem" placeholder="Enter text" />
3737
<button (click)="addItem()">Create</button>
3838
<ul>
39-
@for (task of tasks.data(); track task) {
39+
@for (task of tasks.data(); track $index) {
4040
<li>{{ task }}</li>
4141
}
4242
</ul>

packages/angular-query-experimental/src/__tests__/inject-query.test.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,20 @@ describe('injectQuery', () => {
556556

557557
test('should throw when throwOnError is true', async () => {
558558
const zone = TestBed.inject(NgZone)
559-
const errorPromise = new Promise<Error>((resolve) => {
559+
const zoneErrorPromise = new Promise<Error>((resolve) => {
560560
const sub = zone.onError.subscribe((error) => {
561561
sub.unsubscribe()
562562
resolve(error as Error)
563563
})
564564
})
565+
let handler: ((error: Error) => void) | null = null
566+
const processErrorPromise = new Promise<Error>((resolve) => {
567+
handler = (error: Error) => {
568+
process.off('uncaughtException', handler!)
569+
resolve(error)
570+
}
571+
process.on('uncaughtException', handler)
572+
})
565573

566574
@Component({
567575
selector: 'app-test',
@@ -580,18 +588,33 @@ describe('injectQuery', () => {
580588

581589
TestBed.createComponent(TestComponent).detectChanges()
582590

583-
await vi.runAllTimersAsync()
584-
await expect(errorPromise).resolves.toEqual(Error('Some error'))
591+
try {
592+
await vi.runAllTimersAsync()
593+
await expect(zoneErrorPromise).resolves.toEqual(Error('Some error'))
594+
await expect(processErrorPromise).resolves.toEqual(Error('Some error'))
595+
} finally {
596+
if (handler) {
597+
process.off('uncaughtException', handler)
598+
}
599+
}
585600
})
586601

587602
test('should throw when throwOnError function returns true', async () => {
588603
const zone = TestBed.inject(NgZone)
589-
const errorPromise = new Promise<Error>((resolve) => {
604+
const zoneErrorPromise = new Promise<Error>((resolve) => {
590605
const sub = zone.onError.subscribe((error) => {
591606
sub.unsubscribe()
592607
resolve(error as Error)
593608
})
594609
})
610+
let handler: ((error: Error) => void) | null = null
611+
const processErrorPromise = new Promise<Error>((resolve) => {
612+
handler = (error: Error) => {
613+
process.off('uncaughtException', handler!)
614+
resolve(error)
615+
}
616+
process.on('uncaughtException', handler)
617+
})
595618

596619
@Component({
597620
selector: 'app-test',
@@ -610,8 +633,15 @@ describe('injectQuery', () => {
610633

611634
TestBed.createComponent(TestComponent).detectChanges()
612635

613-
await vi.runAllTimersAsync()
614-
await expect(errorPromise).resolves.toEqual(Error('Some error'))
636+
try {
637+
await vi.runAllTimersAsync()
638+
await expect(zoneErrorPromise).resolves.toEqual(Error('Some error'))
639+
await expect(processErrorPromise).resolves.toEqual(Error('Some error'))
640+
} finally {
641+
if (handler) {
642+
process.off('uncaughtException', handler)
643+
}
644+
}
615645
})
616646
})
617647

packages/angular-query-experimental/src/create-base-query.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function createBaseQuery<
9999
>['notifyOnChangeProps'],
100100
) => {
101101
if (!observer) {
102-
throw new Error('Observer is not initialized')
102+
throw new Error(OBSERVER_NOT_READY_ERROR)
103103
}
104104

105105
const trackedResult = observer.trackResult(result)
@@ -125,7 +125,7 @@ export function createBaseQuery<
125125
}
126126
}
127127

128-
const createOrUpdateObserver = (
128+
const setObserverOptions = (
129129
options: DefaultedQueryObserverOptions<
130130
TQueryFnData,
131131
TError,
@@ -134,14 +134,23 @@ export function createBaseQuery<
134134
TQueryKey
135135
>,
136136
) => {
137-
if (observer) {
137+
if (!observer) {
138+
observer = new Observer(queryClient, options)
139+
destroyRef.onDestroy(() => {
140+
destroyed = true
141+
stopPendingTask()
142+
})
143+
} else {
138144
observer.setOptions(options)
139-
return
140145
}
146+
}
141147

142-
observer = new Observer(queryClient, options)
148+
const subscribeToObserver = () => {
149+
if (!observer) {
150+
throw new Error(OBSERVER_NOT_READY_ERROR)
151+
}
143152

144-
const unsubscribe = observer.subscribe((state) => {
153+
return observer.subscribe((state) => {
145154
if (state.fetchStatus !== 'idle') {
146155
startPendingTask()
147156
} else {
@@ -172,35 +181,37 @@ export function createBaseQuery<
172181
})
173182
})
174183
})
175-
destroyRef.onDestroy(() => {
176-
destroyed = true
177-
unsubscribe()
178-
stopPendingTask()
179-
})
180184
}
181185

182186
const resultSignal = linkedSignal({
183187
source: defaultedOptionsSignal,
184188
computation: () => {
185-
if (!observer)
186-
throw new Error(
187-
'injectQuery: QueryObserver not initialized yet. Avoid reading the query result during construction',
188-
)
189+
if (!observer) throw new Error(OBSERVER_NOT_READY_ERROR)
189190
const defaultedOptions = defaultedOptionsSignal()
190191
const result = observer.getOptimisticResult(defaultedOptions)
191192
return trackObserverResult(result, defaultedOptions.notifyOnChangeProps)
192193
},
193194
})
194195

195-
// Effect to initialize the observer and set options when options change
196196
effect(() => {
197197
const defaultedOptions = defaultedOptionsSignal()
198-
if (isRestoring()) return
199-
200198
untracked(() => {
201-
createOrUpdateObserver(defaultedOptions)
199+
setObserverOptions(defaultedOptions)
200+
})
201+
})
202+
203+
effect((onCleanup) => {
204+
if (isRestoring()) {
205+
return
206+
}
207+
const unsubscribe = untracked(() => subscribeToObserver())
208+
onCleanup(() => {
209+
unsubscribe()
210+
stopPendingTask()
202211
})
203212
})
204213

205214
return signalProxy(resultSignal.asReadonly())
206215
}
216+
const OBSERVER_NOT_READY_ERROR =
217+
'injectQuery: QueryObserver not initialized yet. Avoid reading the query result during construction'

0 commit comments

Comments
 (0)