Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prefetchInRender causes infinite render loop with deferred value if a promise rejects #8249

Open
ali-idrizi opened this issue Nov 4, 2024 · 0 comments

Comments

@ali-idrizi
Copy link

Describe the bug

When the query of a deferred component rejects, it falls into an infinite render loop. This is consistent and I have written a test case.

I was waiting to see if this was being caused by #8219, but it appears not. Here's the test:

it.only('should throw error if query fails with deferred value', async () => {
  function MyComponent(props: { promise: Promise<string> }) {
    const data = React.use(props.promise)

    return <>{data}</>
  }

  const key = queryKey()
  let renderCount = 0

  function Page() {
    renderCount++

    const [_count, setCount] = React.useState(0)
    const count = React.useDeferredValue(_count)

    const query = useQuery({
      queryKey: [key, count],
      queryFn: async () => {
        await sleep(1)
        // succeed only on first query
        if (count === 0) {
          return 'test' + count
        }
        throw new Error('Error test')
      },
      retry: false,
    })

    return (
      <React.Suspense fallback="loading..">
        <button onClick={() => setCount((curr) => curr + 1)}>inc</button>
        <MyComponent promise={query.promise} />
      </React.Suspense>
    )
  }

  const rendered = renderWithClient(
    queryClient,
    <ErrorBoundary fallbackRender={() => <div>error boundary</div>}>
      <Page />
    </ErrorBoundary>,
  )

  await waitFor(() => rendered.getByText('loading..'))
  await waitFor(() => rendered.getByText('test0'))

  const consoleMock = vi
    .spyOn(console, 'error')
    .mockImplementation(() => undefined)

  fireEvent.click(rendered.getByText('inc'))

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

  consoleMock.mockRestore()

  // unsure what the exact render count should be, but it should be less than 10
  // it's 6 without `useDeferredValue`, when test passes
  expect(renderCount).toBeLessThan(10)
})

It currently fails waiting for the error boundary because of the infinite render. If you catch the waitFor throw, you will notice very large values for renderCount:

AssertionError: expected 5828 to be less than 10

The same issue occurs when removing useDeferredValue, and updating the count within a startTransition instead.

Your minimal, reproducible example

Failing test provided

Steps to reproduce

N/A

Expected behavior

N/A

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

OS: Windows 11
Browser: Chrome
Version: 129.0

Tanstack Query adapter

react-query

TanStack Query version

06e315c

TypeScript version

No response

Additional context

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant