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

useLazyQuery: fix rules of React violations #11851

Merged
merged 4 commits into from
May 21, 2024

Conversation

phryneas
Copy link
Member

I'm splitting #11511 up a bit, this is the first of those PRs.

Copy link

changeset-bot bot commented May 17, 2024

🦋 Changeset detected

Latest commit: 76693c1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@apollo/client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

github-actions bot commented May 17, 2024

size-limit report 📦

Path Size
dist/apollo-client.min.cjs 38.68 KB (+0.09% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" 47.46 KB (+0.08% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" (production) 45.03 KB (+0.09% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" 34.2 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" (production) 32.05 KB (0%)
import { ApolloProvider } from "dist/react/index.js" 1.23 KB (0%)
import { ApolloProvider } from "dist/react/index.js" (production) 1.22 KB (0%)
import { useQuery } from "dist/react/index.js" 5.29 KB (+0.15% 🔺)
import { useQuery } from "dist/react/index.js" (production) 4.37 KB (+0.05% 🔺)
import { useLazyQuery } from "dist/react/index.js" 5.57 KB (+0.85% 🔺)
import { useLazyQuery } from "dist/react/index.js" (production) 4.64 KB (+0.96% 🔺)
import { useMutation } from "dist/react/index.js" 3.52 KB (0%)
import { useMutation } from "dist/react/index.js" (production) 2.74 KB (0%)
import { useSubscription } from "dist/react/index.js" 3.21 KB (0%)
import { useSubscription } from "dist/react/index.js" (production) 2.4 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" 5.44 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" (production) 4.1 KB (0%)
import { useBackgroundQuery } from "dist/react/index.js" 4.96 KB (0%)
import { useBackgroundQuery } from "dist/react/index.js" (production) 3.61 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" 5.07 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" (production) 3.72 KB (0%)
import { useReadQuery } from "dist/react/index.js" 3.33 KB (0%)
import { useReadQuery } from "dist/react/index.js" (production) 3.27 KB (0%)
import { useFragment } from "dist/react/index.js" 2.29 KB (0%)
import { useFragment } from "dist/react/index.js" (production) 2.23 KB (0%)

@phryneas phryneas force-pushed the pr/rules-of-hooks/useLazyQuery branch from 73571f9 to b328604 Compare May 17, 2024 12:29
Copy link

netlify bot commented May 17, 2024

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit 73571f9
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/66474d5b94a2620008ddbb83
😎 Deploy Preview https://deploy-preview-11851--apollo-client-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.

called: !!execOptionsRef.current,
});

const { forceUpdateState, obsQueryFields } = internalState;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internalState.obsQueryFields contains all the methods we need, and unlike result (where they are spread in), it's stable.

}, []);

Object.assign(result, eagerMethods);
}, [forceUpdateState, obsQueryFields]);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this, these methods would never follow switching clients.

@@ -147,7 +151,7 @@ export function useLazyQuery<

return promise;
},
[]
[eagerMethods, initialFetchPolicy, internalState]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this, execute would never follow switching clients.

) {
stateRef.current = new InternalState(client, query, stateRef.current);
}
const state = stateRef.current;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a rule of React violation and would cause bugs in scenario where a client changed instance in a suspended subtree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance we could have a test that does this so we ensure a future rewrite of this hook doesn't break things?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I wouldn't know where to start testing that - this is extremely theoretical 😅

Comment on lines +125 to +135
let [state, updateState] = React.useState(createInternalState);

if (client !== state.client || query !== state.query) {
// If the client or query have changed, we need to create a new InternalState.
// This will trigger a re-render with the new state, but it will also continue
// to run the current render function to completion.
// Since we sometimes trigger some side-effects in the render function, we
// re-assign `state` to the new state to ensure that those side-effects are
// triggered with the new state.
updateState((state = createInternalState(state)));
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with useState and an in-render setState call instead, see https://react.dev/reference/react/useState#storing-information-from-previous-renders

@@ -109,23 +109,30 @@ export function useInternalState<TData, TVariables extends OperationVariables>(
client: ApolloClient<any>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulling that hook into this PR because useLazyQuery contained a bunch of untrue assumptions about internalState's referential stability.

Copy link

netlify bot commented May 17, 2024

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit 76693c1
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/664c5e3a0eee3b000887a63d
😎 Deploy Preview https://deploy-preview-11851--apollo-client-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
Member

@jerelmiller jerelmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me! Thanks for going through this. We've needed that lint rule for a while now 😆

@github-actions github-actions bot added the auto-cleanup 🤖 label May 17, 2024
@phryneas phryneas merged commit 45c47be into main May 21, 2024
33 checks passed
@phryneas phryneas deleted the pr/rules-of-hooks/useLazyQuery branch May 21, 2024 09:05
@github-actions github-actions bot mentioned this pull request May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants