From 6e79eaf7399170611bcbefda0082947b629f2693 Mon Sep 17 00:00:00 2001 From: Maciej Sawicki Date: Wed, 30 Apr 2025 12:02:03 +0200 Subject: [PATCH] fix(core): reading resource value after reload in the error state (#61441) When the resource is loading after reloading from the error state reading `Resource.value()` would return the default value instead of throwing an error. This change prevents `Resource.hasValue()` from throwing an error in such a case. PR Close #61441 --- packages/core/src/resource/resource.ts | 5 +++ packages/core/test/resource/resource_spec.ts | 42 ++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/packages/core/src/resource/resource.ts b/packages/core/src/resource/resource.ts index 6bb72d3289a..6ed4b3f14e6 100644 --- a/packages/core/src/resource/resource.ts +++ b/packages/core/src/resource/resource.ts @@ -187,6 +187,11 @@ export class ResourceImpl extends BaseWritableResource implements Resou return defaultValue; } + // Prevents `hasValue()` from throwing an error when a reload happened in the error state + if (this.state().status === 'loading' && this.error()) { + return defaultValue; + } + if (!isResolved(streamValue)) { if (throwErrorsFromValue) { throw new ResourceValueError(this.error()!); diff --git a/packages/core/test/resource/resource_spec.ts b/packages/core/test/resource/resource_spec.ts index cf5080ceb07..dfb4a857cc4 100644 --- a/packages/core/test/resource/resource_spec.ts +++ b/packages/core/test/resource/resource_spec.ts @@ -835,6 +835,48 @@ describe('resource', () => { expect(echoResource.error()).toBe(undefined); expect(aborted).toEqual([{counter: 0}, {counter: 0}]); }); + + it('should return default value when getting a value after reloading in the error state', async () => { + const backend = new MockEchoBackend(); + const requestParam = {}; + const echoResource = resource({ + params: () => requestParam, + loader: (params) => backend.fetch(params.params), + injector: TestBed.inject(Injector), + defaultValue: 'my-default-value', + }); + + TestBed.tick(); + await backend.reject(requestParam, new Error('Something went wrong....')); + + expect(echoResource.status()).toBe('error'); + expect(echoResource.isLoading()).toBeFalse(); + expect(echoResource.hasValue()).toBeFalse(); + + const errFromValue = extractError(() => echoResource.value())!; + expect(errFromValue.message).toContain('Resource'); + expect(errFromValue.message).toContain('Something went wrong....'); + expect(errFromValue.cause).toEqual(new Error('Something went wrong....')); + expect(echoResource.error()!).toEqual(new Error('Something went wrong....')); + + echoResource.reload(); + TestBed.tick(); + + expect(echoResource.status()).toBe('reloading'); + expect(echoResource.isLoading()).toBeTrue(); + expect(echoResource.hasValue()).toBeTrue(); + expect(echoResource.value()).toEqual('my-default-value'); + expect(echoResource.error()).toEqual(new Error('Something went wrong....')); + + await backend.flush(); + TestBed.tick(); + + expect(echoResource.status()).toBe('resolved'); + expect(echoResource.isLoading()).toBeFalse(); + expect(echoResource.hasValue()).toBeTrue(); + expect(echoResource.value()).toEqual({}); + expect(echoResource.error()).toEqual(undefined); + }); }); function flushMicrotasks(): Promise {