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

Add the ability to preload a query outside of React #11412

Merged
merged 213 commits into from
Dec 18, 2023

Conversation

jerelmiller
Copy link
Member

@jerelmiller jerelmiller commented Dec 5, 2023

Adds the ability to preload a query outside of React to begin fetching as early as possible. To utilize this capability, you first create a preload function that accepts a client as input. Then call this function with a query and variables/options to create a query ref. This can then be passed to useReadQuery which will suspend until the data is loaded and respond with cache updates.

const client = new ApolloClient({ ... })
const preloadQuery = createQueryPreloader(client)

// start loading a query early
const queryRef = preloadQuery(QUERY, { variables, ...otherOptions })

function App() {
  return (
    <Suspense fallback={<div>Loading</div>}>
      <MyQuery />
    </Suspense>
  )
}

function MyQuery() {
  // Will suspend while the `queryRef` is fetching.
  const { data } = useReadQuery(queryRef)

  // ...
}

This API was created with React Router loader functions in mind. This unlocks the possibility of fetching data inside loader functions without the tradeoff of missing cache updates in your query components.

export const loader = () => {
  const queryRef = preloadQuery(QUERY, { variables, ...otherOptions })

  return queryRef
}

export function RouteComponent() => {
  const queryRef = useLoaderData()
  const { data } = useReadQuery(queryRef)

  // ...
}

One super power of React Router is that it can leave your old UI up while the new one is loading. This is done by simply using the await keyword inside of your loader. To take advantage of this functionality, queryRef now has the capability to await its promise.

export const loader = () => {
  const queryRef = preloadQuery(QUERY)

  await queryRef.toPromise()
  
  return queryRef
}

Copy link

changeset-bot bot commented Dec 5, 2023

🦋 Changeset detected

Latest commit: 937ddda

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 Minor

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 Dec 5, 2023

size-limit report 📦

Path Size
dist/apollo-client.min.cjs 38.22 KB (+0.76% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" 46.04 KB (+1.08% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" (production) 43.59 KB (+1.14% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" 33.96 KB (+0.05% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" (production) 31.89 KB (+0.05% 🔺)
import { ApolloProvider } from "dist/react/index.js" 1.24 KB (0%)
import { ApolloProvider } from "dist/react/index.js" (production) 1.22 KB (0%)
import { useQuery } from "dist/react/index.js" 5.2 KB (0%)
import { useQuery } from "dist/react/index.js" (production) 4.27 KB (0%)
import { useLazyQuery } from "dist/react/index.js" 5.51 KB (0%)
import { useLazyQuery } from "dist/react/index.js" (production) 4.58 KB (0%)
import { useMutation } from "dist/react/index.js" 3.51 KB (0%)
import { useMutation } from "dist/react/index.js" (production) 2.73 KB (0%)
import { useSubscription } from "dist/react/index.js" 3.19 KB (0%)
import { useSubscription } from "dist/react/index.js" (production) 2.39 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" 5.28 KB (+1.85% 🔺)
import { useSuspenseQuery } from "dist/react/index.js" (production) 3.94 KB (+2.39% 🔺)
import { useBackgroundQuery } from "dist/react/index.js" 4.76 KB (-0.19% 🔽)
import { useBackgroundQuery } from "dist/react/index.js" (production) 3.41 KB (-0.66% 🔽)
import { useLoadableQuery } from "dist/react/index.js" 4.98 KB (-0.3% 🔽)
import { useLoadableQuery } from "dist/react/index.js" (production) 3.64 KB (-0.75% 🔽)
import { useReadQuery } from "dist/react/index.js" 3.04 KB (+0.91% 🔺)
import { useReadQuery } from "dist/react/index.js" (production) 2.97 KB (+0.8% 🔺)
import { useFragment } from "dist/react/index.js" 2.11 KB (0%)
import { useFragment } from "dist/react/index.js" (production) 2.06 KB (0%)

@phryneas
Copy link
Member

phryneas commented Dec 5, 2023

This ends up as a weird bundling change - either the SuspenseCache was not part of the react entrypoint before (which I doubt), or it's in there twice now?
https://github.com/apollographql/apollo-client/actions/runs/7095049003?pr=11412

@phryneas
Copy link
Member

I'm suggesting a couple of added @inheritDoc blocks. Those will need an additional change to config/inlineInheritDoc.ts:

diff --git a/config/inlineInheritDoc.ts b/config/inlineInheritDoc.ts
index 5222dafde..5c94a5336 100644
--- a/config/inlineInheritDoc.ts
+++ b/config/inlineInheritDoc.ts
@@ -128,7 +128,11 @@ function processComments() {
   const sourceFiles = project.addSourceFilesAtPaths("dist/**/*.d.ts");
   for (const file of sourceFiles) {
     file.forEachDescendant((node) => {
-      if (Node.isPropertySignature(node)) {
+      if (
+        Node.isPropertySignature(node) ||
+        Node.isMethodSignature(node) ||
+        Node.isCallSignatureDeclaration(node)
+      ) {
         const docsNode = node.getJsDocs()[0];
         if (!docsNode) return;
         const oldText = docsNode.getInnerText();

@phryneas
Copy link
Member

Some more thoughts:

  • should useBackgroundQuery still call retain?
  • should useBackgroundQuery call useQueryRefHandlers to prevent code duplication? (Might be cause another in-render-reexecution of the rendering component though).

Copy link
Member

@phryneas phryneas left a comment

Choose a reason for hiding this comment

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

This is looking very good, just some minor nits left! (And those can also be follow-ups!)

@github-actions github-actions bot added the auto-cleanup 🤖 label Dec 18, 2023
@jerelmiller
Copy link
Member Author

We need to make sure #11426 makes it into release-3.9. We will either redo that one, or find a way to rebase

@jerelmiller jerelmiller merged commit 58db5c3 into release-3.9 Dec 18, 2023
31 of 32 checks passed
@jerelmiller jerelmiller deleted the jerel/load-query-outside-react branch December 18, 2023 17:07
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-cleanup 🤖 🥚 backwards-compatible for PRs that do not introduce any breaking changes ⏲️ react-suspense
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants