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

feat: add useBackgroundQuery and useReadQuery Suspense hooks #10755

Merged
merged 40 commits into from
May 15, 2023

Conversation

alessbell
Copy link
Contributor

@alessbell alessbell commented Apr 11, 2023

Adds useBackgroundQuery and useReadQuery hooks as companion hooks to useSuspenseQuery for fetching and reading data within React Suspense boundaries.

useBackgroundQuery

useBackgroundQuery allows a user to initiate a query as "high up" in their application's render tree as possible, decoupled from the site where the returned data is consumed, which will trigger the nearest Suspense boundary while the query is pending.

The hook accepts query and options as positional arguments, the latter extending SuspenseQueryHookOptions while omitting returnPartialData and refetchWritePolicy for a slightly smaller initial API surface. The hook returns a queryRef as well as fetchMore and refetch functions.

queryRef

In order to link a query initiated by a specific useBackgroundQuery call to the place its data are consumed—which can be uniquely identified not only by the query and variables passed, but also the optional queryKey supplied by the user—the hook needs to return a reference to the query that can be read later on.

A queryRef is a QuerySubscription instance that holds references to the promises that are read or thrown by React's use (or Apollo Client's __use if React.use is not detected).

useReadQuery

useReadQuery accepts a queryRef and returns data, networkStatus and error.

networkStatus is only useful in certain edge cases since the vast majority of the time the component is in a suspended state when going to the network via loading/refetching. One of these cases is captured in the test does not suspend deferred queries with data in the cache and using a "cache-and-network" fetch policy: the networkStatus on initial render is loading since the component does not suspend when the entire query is already available in the cache.

Hello World Example

interface QueryData {
  foo: { bar: string };
}

const suspenseCache = new SuspenseCache();

const client = new ApolloClient({
  uri: "http://localhost:4000/graphql",
  cache: new InMemoryCache()
});

function SuspenseFallback() {
  return <div>Loading...</div>;
}

function Child({ queryRef }: { queryRef: QuerySubscription<QueryData> }) {
  const { data } = useReadQuery<QueryData>(queryRef);

  return <div>{data.foo.bar}</div>;
}

function Parent() {
  const [queryRef] = useBackgroundQuery(query);

  return <Child queryRef={queryRef} />;
}

function App() {
  return (
    <ApolloProvider client={client} suspenseCache={suspenseCache}>
      <ErrorBoundary fallback={<div>Error</div>}>
        <Suspense fallback={<SuspenseFallback />}>
          <Parent />
        </Suspense>
      </ErrorBoundary>
    </ApolloProvider>
  );
}

Future PRs

@changeset-bot
Copy link

changeset-bot bot commented Apr 11, 2023

🦋 Changeset detected

Latest commit: 46ddb18

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

@alessbell alessbell requested a review from jerelmiller April 11, 2023 14:33
@jerelmiller jerelmiller changed the base branch from start-transition-with-refetch to release-3.8 April 11, 2023 20:10
@jerelmiller jerelmiller changed the base branch from release-3.8 to start-transition-with-refetch April 11, 2023 20:11
@alessbell alessbell force-pushed the issue-8694--useBackgroundQuery branch from 85e047d to ffb3538 Compare April 14, 2023 15:34
@alessbell alessbell changed the base branch from start-transition-with-refetch to suspense-refetch May 2, 2023 21:25
@alessbell alessbell changed the base branch from suspense-refetch to start-transition-with-refetch May 2, 2023 21:25
@alessbell alessbell force-pushed the issue-8694--useBackgroundQuery branch from c37dfd7 to 438fa99 Compare May 8, 2023 19:07
@alessbell alessbell changed the base branch from start-transition-with-refetch to release-3.8 May 8, 2023 19:07
@alessbell alessbell force-pushed the issue-8694--useBackgroundQuery branch from 438fa99 to bb6954a Compare May 8, 2023 19:09
@alessbell alessbell force-pushed the issue-8694--useBackgroundQuery branch from bb6954a to 76a3193 Compare May 8, 2023 19:47
@alessbell alessbell changed the title feat: add useBackgroundQuery and useReadQuery hooks feat: add useBackgroundQuery and useReadQuery Suspense hooks May 10, 2023
@alessbell
Copy link
Contributor Author

/release:pr

@github-actions
Copy link
Contributor

A new release has been made for this PR. You can install it with npm i @apollo/[email protected].

@phryneas
Copy link
Member

One thing to keep in mind: we'll have to try this with the Next package, to make sure that those queryRef objects work nicely there, too. I think they should, but let's be sure.

@alessbell
Copy link
Contributor Author

One thing to keep in mind: we'll have to try this with the Next package, to make sure that those queryRef objects work nicely there, too. I think they should, but let's be sure.

Good call-out, added to the list of future work on the PR description that I'll tackle... next 😀

@phryneas
Copy link
Member

phryneas commented May 15, 2023

Looking at this, it might sense for us to pair on this.

Quick intro: take a look at these wrapped hook definitions.
In the Next.js wrapper, we wrap the hook into a version that transports a partial copy of the result from server to browser, to make sure that the browser can render exactly the same HTML with the same data that the server had - at least on first render.
After first render, the values from the wrapped transport are thrown away, and the normal return value of the hooks will be returned.

So, looking at the queryReference class, we would need to transport over most of QueryReference.result, analogous to useQuery.
But there are more values that we should transport over, that might not be possible with the simple wrapping mechanism that we currently use.

Going through the public properties, observable is probably fine, just as version - but we need to take a good look at what we do with promises - do we transport the resolved value of those promises over, and recreate promises on the client side?
What if they already had passed through use, do we need to mock the properties added by use as well?
Note that these problems might go away if we decided that version and promise could be private instead of public, as we then know that no user would just play around with them - and we could manage most of that by just wrapping useReadQuery, while ignoring those problematic properties of QueryReference.

VariablesCaseData,
VariablesCaseVariables
>(query);
const { data: explicit } = useSuspenseQuery(query);
Copy link
Member

@jerelmiller jerelmiller May 15, 2023

Choose a reason for hiding this comment

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

These being type tests, the explicit setting of the generic type here was actually purposeful (hence the name explicit). Essentially I wanted to make sure that setting the type of the generic directly rather than relying on type inference ensured that data was properly typed with the various options you can pass to the hook.

I realize though that I made a mistake and I shouldn't be passing a query here that uses TypedDocumentNode (which make type inference work correctly). I'll update this in a separate PR to use an untyped query document to test against, which is a bit more realistic.

In the mean time, would you mind reverting these useSuspenseQuery type tests back to the original?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, my bad - was moving too fast there. Totally unintended, will revert the changes for the types tests now.

Copy link
Member

Choose a reason for hiding this comment

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

No problem at all! Thanks so much 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

56083fb :)

@github-actions
Copy link
Contributor

github-actions bot commented May 15, 2023

size-limit report 📦

Path Size
dist/apollo-client.min.cjs 35.97 KB (+0.52% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" 45.15 KB (+0.4% 🔺)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" 34.19 KB (0%)
import { ApolloProvider } from "dist/react/index.js" 867 B (0%)
import { useQuery } from "dist/react/index.js" 25.57 KB (0%)
import { useLazyQuery } from "dist/react/index.js" 25.91 KB (0%)
import { useMutation } from "dist/react/index.js" 24.09 KB (0%)
import { useSubscription } from "dist/react/index.js" 2.36 KB (0%)
import { useSuspenseQuery_experimental } from "dist/react/index.js" 3.53 KB (0%)
import { useFragment_experimental } from "dist/react/index.js" 1.78 KB (0%)
import { useBackgroundQuery_experimental } from "dist/react/index.js" 2.97 KB (+100% 🔺)
import { useReadQuery_experimental } from "dist/react/index.js" 1.12 KB (+100% 🔺)

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.

Outside of my last comment, this PR looks great! I'm super excited to get this into an alpha!

@alessbell alessbell merged commit e3c676d into release-3.8 May 15, 2023
@alessbell alessbell deleted the issue-8694--useBackgroundQuery branch May 15, 2023 18:33
@alessbell alessbell restored the issue-8694--useBackgroundQuery branch May 15, 2023 18:37
@alessbell alessbell deleted the issue-8694--useBackgroundQuery branch May 15, 2023 18:38
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants