Skip to content

onComplete is invoked after rendering with loaded data in 3.8.x #11209

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

Open
dfilatov opened this issue Sep 12, 2023 · 7 comments
Open

onComplete is invoked after rendering with loaded data in 3.8.x #11209

dfilatov opened this issue Sep 12, 2023 · 7 comments
Labels
🏓 awaiting-team-response requires input from the apollo team ⁉️ question

Comments

@dfilatov
Copy link

Issue Description

Something has been broken in 3.8.x versions. onCompleted now is invoked after rendering with loaded data, it's unexpected and leads to errors in our application because we need to do some additional work with loaded data before it's rendered. In 3.7.17 version this works as expected.

Link to Reproduction

https://codesandbox.io/s/sad-fermat-djg7mq

Reproduction Steps

  • open https://codesandbox.io/s/sad-fermat-djg7mq
  • take a look at console, you'll see:
    render true
    completed
    render false
  • change version of @apollo/client to 3.8.3, save
  • take a look at console, you'll see:
    render true
    render false
    completed
@phryneas
Copy link
Member

3.8 includes a change that is generally recommended by the React team when dealing with async data sources (like Apollo Client): It calls the handleStoreChange callback of useSyncExternalStore instead of a useState setState callback like before.
As a result, React not applies different timing of things here.

Generally, please note that we don't have a lot of control over the order of operation here - React decides pretty arbitrarily when it rerenders.
We actually have always called the code triggering onCompleted after we trigger the rerender. Until now, it caused the React state update batching behavior to move the next render back a tick. handleStoreChange should also do this (to batch with other external state source updates), but sometimes it doesn't.
Keep in mind, this is "autobatching" is only the case in React 18 - in React 17 you would see the current behaviour even with older versions of Apollo Client.

Once React fixes this bug the timing will likely go back to the behavior you have seen in React 18 with 3.7.

=> We have never been able to guarantee a certain order of operation, and can also not do so going into the future. React decides when it actually rerenders, not us.

Out of curiosity - what are you doing here that needs to happen before a render?

@dfilatov
Copy link
Author

@phryneas Thank you for quick response!

Out of curiosity - what are you doing here that needs to happen before a render?

We are building/committing/uncommitting/resetting our internal models based on query data within onCompleted/onError

@phryneas
Copy link
Member

Hm, if that is local component state, most of the time you should be able to do that synchronously during component render - can you share some example code here? Maybe I can suggest an alternative way of doing so.

@dfilatov
Copy link
Author

dfilatov commented Sep 15, 2023

Very simplified approach looks like:

const MyComponent = ({ model }) => {
    const { loading } = useSomeDataQuery({
        onCompleted(data) {
            model.setData(data);
            model.commit();
        }
    });

    return loading ?
        <Loader/> :
        <ModelView={ model }/>;        
};

I could get the same behaviour with React.useMemo but that looks quite weird and unnatural to me:

const MyComponent = ({ model }) => {
    const { data, loading } = useSomeDataQuery({});
    const modelWithData = React.useMemo(
        () => {
            if(loading) {
                return null;
            }

            model.setData(data);
            model.commit();

            return model;
        },
        [loading, data]
    );

    return loading ?
        <Loader/> :
        <ModelView={ modelWithData }/>;        
};

@bignimbus bignimbus added 🏓 awaiting-team-response requires input from the apollo team ⁉️ question labels Sep 20, 2023
@iamnnort
Copy link

iamnnort commented Oct 2, 2023

The same issue

@Mofo50C
Copy link

Mofo50C commented Dec 20, 2023

Yeah, im having this issue as well. Apollo client v3.8.8, react v18.2, nextjs v14. After a mutation, refetchQueries runs, query is updated, data is returned but onCompleted isn't called so the component state doesn't update. Previously working on 3.7

@ricardopieper
Copy link

ricardopieper commented Jan 11, 2024

Same, updated to Apollo v3.8.6 and this started happening. However I agree with the new behavior. If the onComplete handler took minutes to run, it shouldn't block the component from getting the data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏓 awaiting-team-response requires input from the apollo team ⁉️ question
Projects
None yet
Development

No branches or pull requests

6 participants