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

Using a setState inside a startTransition with a fetchMore cause Apollo to clear cached results when using a policy #11642

Closed
Arno500 opened this issue Mar 5, 2024 · 10 comments · Fixed by #11706
Assignees

Comments

@Arno500
Copy link

Arno500 commented Mar 5, 2024

Issue Description

Hello,
I noticed that whenever I set a state based on the new data received, IF AND ONLY IF the incoming array is empty (???), it resets the results to an empty array for some reason

Link to Reproduction

https://stackblitz.com/edit/stackblitz-starters-hswnfx?file=src%2Fcomponents%2FPaginatedQuery.tsx

Reproduction Steps

  1. Use an awaited fetchMore inside a startTransition
  2. Continue to fetchMore until you get a perfectly empty array, signifying that you got to the end of the results
  3. Set some state just after the awaited fetchMore (here we use a hasMore, similar to our own code base)
  4. Observe the resulting merged array in data being 'reset'

@apollo/client version

3.9.5

@jerelmiller
Copy link
Member

Hey @Arno500 👋

I'll dig into this a bit more deeply, but as an FYI, something that stands out immediately is that React doesn't support async functions inside startTransition. From the React docs:

The function you pass to startTransition must be synchronous. React immediately executes this function, marking all state updates that happen while it executes as transitions. If you try to perform more state updates later (for example, in a timeout), they won’t be marked as transitions.

Its possible something is happening here that gets React in a weird state. Transitions essentially render your component in 2 different states at the "same time", one with the new value, and one with the old value. Its possible the async function might be doing something weird here and confusing React.

I'll play around with your demo a bit to understand if this is the root issue, but just wanted you to know that React doesn't support async functions for startTransition 🙂

@Arno500
Copy link
Author

Arno500 commented Mar 5, 2024

Thank you very much for that thorough explanation. Then how would you recommend reacting on only the new values?
I mean, in this case, I want to check that the incoming value (and not the total data) is empty. So how would it pan out with Apollo?

@jerelmiller
Copy link
Member

It would mostly be a matter of moving some code around.

Here is a version that uses a synchronous callback, but allows you to wait for the data to finish:

  const fetchMoreData = async () => {
    console.log('Probably have more data: ', hasMore);
    if (!hasMore) return;
    let promise!: Promise<ApolloQueryResult<any>>;

    startTransition(() => {
      promise = fetchMore({
        variables: {
          limit: 5,
          offset: data.histories.length,
        },
      });
    });

    const { data: newData } = await promise;

    console.log('Just received data: ', newData);
    if (newData.histories.length < 1) {
      console.log('Out of data, we consumed everything');
      setHasMore(false);
    }
  };

I threw this into my own fork of the reproduction, but looks like it doesn't really change much. So it seems like its likely an Apollo Client bug!

@DenisLaboureyras
Copy link

I can reproduce this issue in my code as well.

I used this workaround of using readyQuery to solve temporarily this issue :
#11315 (comment)

@phryneas
Copy link
Member

phryneas commented Mar 8, 2024

@DenisLaboureyras can you please verify that you are still experiencing this in 3.9.6?

@DenisLaboureyras
Copy link

@phryneas yes unfortunately i'm still experiencing this in 3.9.6.
As stated by @Arno500, the last fetchMore with an empty incoming array is reseting the cache

@jerelmiller
Copy link
Member

Some discovery notes:

I think I've narrowed this down to something in QueryReference itself. We are emmitting the wrong result on the promise. The cache seems fine and contains the full data set, we just are giving the wrong value from the hook.

@jerelmiller
Copy link
Member

Hey @Arno500 👋

I've got a fix for this issue in #11706 and have confirmed this addresses the problem. See my fork of the reproduction which uses a snapshot build from that PR for a working example.

Copy link
Contributor

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.

Copy link
Contributor

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
4 participants