query icon indicating copy to clipboard operation
query copied to clipboard

`queryClient.clear()` breaks with suspense and when query goes from success to fail

Open flybayer opened this issue 4 years ago • 1 comments

Describe the bug

Using queryClient.clear() with suspense enabled causes an infinite loop. We encountered this in Blitz on user logout: https://github.com/blitz-js/blitz/pull/2249

To Reproduce

Add the following to src/react/tests/suspense.test.tsx

  it('should render error boundary when query goes from success to fail with clear()', async () => {
    const key = queryKey()

    let succeed = true
    const consoleMock = mockConsoleError()

    function Page() {
      useQuery(
        key,
        async () => {
          console.log('query')
          await sleep(10)
          if (!succeed) {
            throw new Error('Suspense Error Bingo')
          } else {
            return 'data'
          }
        },
        {
          retry: false,
          suspense: true,
        }
      )
      return <div>rendered</div>
    }

    function App() {
      const { reset } = useQueryErrorResetBoundary()
      return (
        <ErrorBoundary
          onReset={reset}
          fallbackRender={({ resetErrorBoundary }) => (
            <div>
              <div>error boundary</div>
              <button
                onClick={() => {
                  resetErrorBoundary()
                }}
              >
                retry
              </button>
            </div>
          )}
        >
          <React.Suspense fallback="Loading...">
            <Page />
          </React.Suspense>
        </ErrorBoundary>
      )
    }

    const rendered = renderWithClient(queryClient, <App />)

    await waitFor(() => rendered.getByText('Loading...'))
    await waitFor(() => rendered.getByText('rendered'))

    succeed = false
    queryClient.clear()
    await waitFor(() => rendered.getByText('error boundary'))
    consoleMock.mockRestore()
  })

Expected behavior

Should Work

Additional context

React-query 3.13.9

Workaround

-queryClient.clear()
+await queryClient.cancelQueries()
+await queryClient.resetQueries()
+queryClient.getMutationCache().clear()

@tannerlinsley @TkDodo

flybayer avatar Apr 20 '21 20:04 flybayer

@flybayer on the latest master, the provided test case times out with:

Error: Unable to find an element with the text: error boundary.

at

await waitFor(() => rendered.getByText('error boundary'))

and I think that's the correct behaviour. By just calling queryClient.clear, we'll remove all entries from the cache, but that doesn't inform active observers. So nothing re-renders the Page, so it doesn't refetch and also doesn't show the error boundary.

can you check if we maybe fixed the issue with the recent changes to shouldFetchOptionally, maybe this issue is obsolete now? Thanks

TkDodo avatar Aug 12 '21 18:08 TkDodo

closing as this is pretty stale and the provided test case doesn't behave as described anymore. Please reopen with a new reproduction if necessary.

TkDodo avatar Sep 03 '22 20:09 TkDodo