From e88631d0d317134364cd55a98605203fb1e6c9c3 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 20 Nov 2021 08:22:09 +0100 Subject: [PATCH] fix(useQuery): fix tracked queries and error boundaries if you useErrorBoundary, you're likely not observing the error, because you're not using the returned error field - the ErrorBoundary handles the error for you. So when the actual error then happens, we don't inform the observer about it, because we're only informing them if a prop has changed that they are interested in, resulting in the error boundary never being shown. The solution would be to always track the error property if you've set useErrorBoundary --- src/core/queryObserver.ts | 26 ++++++-- .../tests/QueryResetErrorBoundary.test.tsx | 60 +++++++++++++++++++ src/react/useBaseQuery.ts | 2 +- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index f1ab5c8d2b..aa36cfd2cd 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -232,24 +232,38 @@ export class QueryObserver< } trackResult( - result: QueryObserverResult + result: QueryObserverResult, + defaultedOptions: QueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > ): QueryObserverResult { const trackedResult = {} as QueryObserverResult + const trackProp = (key: keyof QueryObserverResult) => { + if (!this.trackedProps.includes(key)) { + this.trackedProps.push(key) + } + } + Object.keys(result).forEach(key => { Object.defineProperty(trackedResult, key, { configurable: false, enumerable: true, get: () => { - const typedKey = key as keyof QueryObserverResult - if (!this.trackedProps.includes(typedKey)) { - this.trackedProps.push(typedKey) - } - return result[typedKey] + trackProp(key as keyof QueryObserverResult) + return result[key as keyof QueryObserverResult] }, }) }) + if (defaultedOptions.useErrorBoundary || defaultedOptions.suspense) { + trackProp('error') + } + return trackedResult } diff --git a/src/react/tests/QueryResetErrorBoundary.test.tsx b/src/react/tests/QueryResetErrorBoundary.test.tsx index f73210d251..5fc1e366fe 100644 --- a/src/react/tests/QueryResetErrorBoundary.test.tsx +++ b/src/react/tests/QueryResetErrorBoundary.test.tsx @@ -592,4 +592,64 @@ describe('QueryErrorResetBoundary', () => { expect(rendered.queryByText('page')).not.toBeNull() }) + + it('should show error boundary when using tracked queries even though we do not track the error field', async () => { + const key = queryKey() + + let succeed = false + const consoleMock = mockConsoleError() + + function Page() { + const { data } = useQuery( + key, + async () => { + await sleep(10) + if (!succeed) { + throw new Error('Error') + } else { + return 'data' + } + }, + { + retry: false, + useErrorBoundary: true, + notifyOnChangeProps: 'tracked', + } + ) + return
{data}
+ } + + const rendered = renderWithClient( + queryClient, + + {({ reset }) => ( + ( +
+
error boundary
+ +
+ )} + > + +
+ )} +
+ ) + + await waitFor(() => rendered.getByText('error boundary')) + await waitFor(() => rendered.getByText('retry')) + succeed = true + fireEvent.click(rendered.getByText('retry')) + await waitFor(() => rendered.getByText('data')) + + consoleMock.mockRestore() + }) }) diff --git a/src/react/useBaseQuery.ts b/src/react/useBaseQuery.ts index 693944fc38..3adb64e053 100644 --- a/src/react/useBaseQuery.ts +++ b/src/react/useBaseQuery.ts @@ -144,7 +144,7 @@ export function useBaseQuery< // Handle result property usage tracking if (defaultedOptions.notifyOnChangeProps === 'tracked') { - result = observer.trackResult(result) + result = observer.trackResult(result, defaultedOptions) } return result