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

react-query previousData in placeholderData ignores requests that hit cache #6341

Closed
TkDodo opened this issue Nov 9, 2023 Discussed in #6340 · 3 comments · Fixed by #6357
Closed

react-query previousData in placeholderData ignores requests that hit cache #6341

TkDodo opened this issue Nov 9, 2023 Discussed in #6340 · 3 comments · Fixed by #6357
Assignees
Labels
bug Something isn't working package: query-core

Comments

@TkDodo
Copy link
Collaborator

TkDodo commented Nov 9, 2023

Discussed in #6340

Originally posted by kimwes November 8, 2023
I'm trying to use useQuery placeholderData to avoid flickering content when querying for new content. These queries are made after a debounced input change that acts as part of the query key. The problem I'm facing is the following flow:

  1. user types something new
  2. user types something new
  3. user types the 1) search term
  4. user types something new and here the placeholder shown is not 1) result as is expected but instead is shown the result from 2) because apparently previousData doesn't use requests that are from cache.

Is this by design or a bug? Any ideas if this will be changed or should I make some sort of a workaround?
This is the same issue as with the person here: #5913 (comment)
Here's a simple test case that shows the problem:

it('should keep the previous data when placeholderData is set and cache is used', async () => {
    const key = queryKey()
    const states: Array<UseQueryResult<number | undefined>> = []
    const steps = [0, 1, 0, 2]

    function Page() {
      const [count, setCount] = React.useState(0)

      const state = useQuery({
        staleTime: Infinity,
        queryKey: [key, steps[count]],
        queryFn: async () => {
          await sleep(10)
          return steps[count]
        },
        placeholderData: keepPreviousData,
      })

      states.push(state)

      return (
        <div>
          <div>data: {state.data}</div>
          <button onClick={() => setCount((c) => c + 1)}>setCount</button>
        </div>
      )
    }

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

    await waitFor(() => rendered.getByText('data: 0'))

    fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))

    await waitFor(() => rendered.getByText('data: 1'))

    fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))

    await waitFor(() => rendered.getByText('data: 0'))

    fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))

    await waitFor(() => rendered.getByText('data: 2'))

    // Initial
    expect(states[0]).toMatchObject({
      data: undefined,
      isFetching: true,
      isSuccess: false,
      isPlaceholderData: false,
    })
    // Fetched
    expect(states[1]).toMatchObject({
      data: 0,
      isFetching: false,
      isSuccess: true,
      isPlaceholderData: false,
    })
    // Set state
    expect(states[2]).toMatchObject({
      data: 0,
      isFetching: true,
      isSuccess: true,
      isPlaceholderData: true,
    })
    // New data
    expect(states[3]).toMatchObject({
      data: 1,
      isFetching: false,
      isSuccess: true,
      isPlaceholderData: false,
    })
    // Set state with existing data
    expect(states[4]).toMatchObject({
      data: 0,
      isFetching: false,
      isSuccess: true,
      isPlaceholderData: false,
    })
    // Set state where the placeholder value should come from cache request
    expect(states[5]).toMatchObject({
      data: 0, // test fails this with a received value of 1 because cache requests are ignored in placeholderData: keepPreviousData
      isFetching: true,
      isSuccess: true,
      isPlaceholderData: true,
    })
    // New data
    expect(states[6]).toMatchObject({
      data: 2,
      isFetching: false,
      isSuccess: true,
      isPlaceholderData: false,
    })
  })
@TkDodo TkDodo added bug Something isn't working package: query-core labels Nov 9, 2023
@TkDodo
Copy link
Collaborator Author

TkDodo commented Nov 9, 2023

@ardeora I can confirm that the above test case fails and I think it shouldn't. Can you take a look at this?

@ardeora
Copy link
Contributor

ardeora commented Nov 9, 2023

Thanks for the reproduction! I'll take a look today or by tomorrow for sure!

@ardeora ardeora self-assigned this Nov 9, 2023
@ardeora
Copy link
Contributor

ardeora commented Nov 11, 2023

An update on this. I think I have found the root cause. Just looking at the best way to solve this right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working package: query-core
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants