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

Keep isSuccess: true when switching to an uninitialized cache entry #4731

Merged
merged 1 commit into from
Nov 23, 2024

Conversation

markerikson
Copy link
Collaborator

This PR:

  • Updates the logic inside of queryStatePreSelector to preserve a value of isSuccess: true if we switch from a valid cache entry to an uninitialized cache entry

Fixes #3353

Investigation

(copied from #3353 )

The flash happens when we:

  • Have an existing fulfilled entry with data
  • There's an arg change that results in a new uninitialized cache entry
  • We have not yet started fetching for the new entry

This test generally replicates the bad behavior.

test.only('`isSuccess` does not jump back false on subsequent queries', async () => {
      const successHist: boolean[] = [],
        fetchingHist: boolean[] = []

      function User({ id }: { id: number }) {
        const queryRes = api.endpoints.getUser.useQuery(id)

        const { isSuccess, isFetching, status } = queryRes

        console.log(
          `id: ${id}\tisFetching: ${queryRes.isFetching}\tisSuccess: ${queryRes.isSuccess}`,
        )

        useEffect(() => {
          successHist.push(isSuccess)
        }, [isSuccess])
        useEffect(() => {
          fetchingHist.push(isFetching)
        }, [isFetching])
        return (
          <div data-testid="status">
            {status === QueryStatus.fulfilled && id}
          </div>
        )
      }

      let { rerender } = render(<User id={1} />, { wrapper: storeRef.wrapper })

      await waitFor(() =>
        expect(screen.getByTestId('status').textContent).toBe('1'),
      )
      rerender(<User id={2} />)

      await waitFor(() =>
        expect(screen.getByTestId('status').textContent).toBe('2'),
      )

      expect(successHist).toEqual([false, true, true, true, true])
      expect(fetchingHist).toEqual([true, false, true, true, false])
    })

and per above, here's the logged output:

id: 1   isFetching: true        isSuccess: false
id: 1   isFetching: true        isSuccess: false
id: 1   isFetching: false       isSuccess: true
id: 2   isFetching: true        isSuccess: false <-----
id: 2   isFetching: true        isSuccess: true
id: 2   isFetching: false       isSuccess: true

Here's the values in scope in the query selector at the time of that {id: 2, isFetching: true, isSuccess: false} line:

{
  currentState: {
    status: 'uninitialized',
    isUninitialized: true,
    isLoading: false,
    isSuccess: false,
    isError: false
  },
  lastResult: {
    status: 'fulfilled',
    endpointName: 'getUser',
    requestId: 'QWSeI6-xRVNKXYeV0Zc0a',
    originalArgs: 1,
    startedTimeStamp: 1732400449088,
    data: { name: 'Timmy' },
    fulfilledTimeStamp: 1732400449239,
    isUninitialized: false,
    isLoading: false,
    isSuccess: true,
    isError: false,
    currentData: { name: 'Timmy' },
    isFetching: false
  },
  queryArgs: 2,
  data: { name: 'Timmy' },
  hasData: true,
  isFetching: false,
  isLoading: false,
  isSuccess: false
}

The existing logic for that is const isSuccess = currentState.isSuccess || (isFetching && hasData).

on the flip side, we also have:

const noPendingQueryStateSelector: QueryStateSelector<any, any> = (
  selected,
) => {
  if (selected.isUninitialized) {
    return {
      ...selected,
      isUninitialized: false,
      isFetching: true,
      isLoading: selected.data !== undefined ? false : true,
      status: QueryStatus.pending,
    } as any
  }
  return selected
}

Given that, it seems that the logic for isSuccess is not taking into account that currentState.isUninitialized case.

Fix

I initially tried doing const isFetching = currentState.isLoading || currentState.isUninitialized, and that fixed the isSuccess logic, but also altered the value of isFetching. So, per Lenz, the right fix is to inline the change into ( (isFetching | currentState.isUninitialized) && hasData).

Copy link

codesandbox bot commented Nov 23, 2024

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

Copy link

netlify bot commented Nov 23, 2024

Deploy Preview for redux-starter-kit-docs ready!

Name Link
🔨 Latest commit 02248d0
🔍 Latest deploy log https://app.netlify.com/sites/redux-starter-kit-docs/deploys/674266a69b5e78000805f590
😎 Deploy Preview https://deploy-preview-4731--redux-starter-kit-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 02248d0:

Sandbox Source
@examples-query-react/basic Configuration
@examples-query-react/advanced Configuration
@examples-action-listener/counter Configuration
rtk-esm-cra Configuration

Copy link

size-limit report 📦

Path Size
1. entry point: @reduxjs/toolkit/query/react (cjs, production.min.cjs) 24.53 KB (+0.39% 🔺)
2. entry point: @reduxjs/toolkit/query/react (without dependencies) (cjs, production.min.cjs) 2.86 KB (+0.04% 🔺)
3. createApi (react) (.modern.mjs) 15.65 KB (+0.03% 🔺)

@markerikson markerikson merged commit 4d92026 into master Nov 23, 2024
74 checks passed
@markerikson markerikson deleted the bugfix/3353-isSuccess-flashing branch November 23, 2024 23:44
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

Successfully merging this pull request may close these issues.

isSuccess flashes false on subsequent queries
1 participant