Skip to content

Commit 81f8fe6

Browse files
alxhubAndrewKushnir
authored andcommitted
refactor(core): port resource() to linkedSignal() (#59024)
When the reactive `request` of a resource() notifies, it transitions to the Loading state, fires the loader, and eventually transitions to Resolved. With the prior implementation, a change of the `request` will queue the effect, but the state remains unchanged until the effect actually runs. For a brief period, the resource is in a state where the request has changed, but the state has yet to update. This is problematic if we want to use resources in certain contexts where we care about the state of the resource in a synchronous way. For example, an async validator backed by a resource might be checked after an update: ``` value.set(123); if (validator.value()) { // value is still valid, even though the resource is dirty and will soon // flip to loading state (returning value(): undefined) while revalidating } ``` To address this timing concern, `linkedSignal()` is used within the `resource()` to synchronously transition the state in response to the request changing. This ensures any followup reads see a consistent view of the resource regardless of whether the effect has run. This also addresses a race condition where `.set()` behaves differently on a `resource()` depending on whether or not the effect has run. PR Close #59024
1 parent 906413a commit 81f8fe6

File tree

3 files changed

+266
-122
lines changed

3 files changed

+266
-122
lines changed

packages/core/rxjs-interop/test/rx_resource_spec.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ describe('rxResource()', () => {
2626
it('should cancel the fetch when a new request comes in', async () => {
2727
const injector = TestBed.inject(Injector);
2828
const appRef = TestBed.inject(ApplicationRef);
29-
let unsub = false;
3029
const request = signal(1);
31-
const res = rxResource({
30+
let unsub = false;
31+
let lastSeenRequest: number = 0;
32+
rxResource({
3233
request,
33-
loader: ({request}) =>
34-
new Observable((sub) => {
34+
loader: ({request}) => {
35+
lastSeenRequest = request;
36+
return new Observable((sub) => {
3537
if (request === 2) {
3638
sub.next(true);
3739
}
@@ -40,12 +42,13 @@ describe('rxResource()', () => {
4042
unsub = true;
4143
}
4244
};
43-
}),
45+
});
46+
},
4447
injector,
4548
});
4649

4750
// Wait for the resource to reach loading state.
48-
await waitFor(() => res.isLoading());
51+
await waitFor(() => lastSeenRequest === 1);
4952

5053
// Setting request = 2 should cancel request = 1
5154
request.set(2);

0 commit comments

Comments
 (0)