Skip to content

Commit

Permalink
Merge pull request #8633 from apollographql/sb/docs-copyedits
Browse files Browse the repository at this point in the history
Copyedits to various AC docs
  • Loading branch information
Stephen Barlow authored Aug 12, 2021
2 parents 7222175 + 04988d2 commit bd03ff0
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 76 deletions.
123 changes: 63 additions & 60 deletions docs/source/caching/advanced-topics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export class Foo extends Component {
export default withApollo(Foo);
```


## Refetching queries after a mutation

In certain cases, writing an `update` function to [update the cache after a mutation](../data/mutations/#updating-local-data) can be complex, or even impossible if the mutation doesn't return modified fields.
Expand All @@ -118,7 +117,68 @@ For details, see [Refetching queries](../data/mutations/#refetching-queries).

> Note that although `refetchQueries` can be faster to implement than an `update` function, it also requires additional network requests that are usually undesirable. For more information, see [this blog post](https://www.apollographql.com/blog/when-to-use-refetch-queries-in-apollo-client/).
## Incremental loading: `fetchMore`
## Cache redirects

In some cases, a query requests data that already exists in the cache under a different reference. For example, your UI might have a list view and a detail view that both use the same data.

The list view might run the following query:

```graphql
query Books {
books {
id
title
abstract
}
}
```

When a specific book is selected, the detail view might display an individual item using this query:

```graphql
query Book($id: ID!) {
book(id: $id) {
id
title
abstract
}
}
```

In a case like this, _we_ know that the second query's data might already be in the cache, but because that data was fetched by a different query, _Apollo Client_ doesn't know that. To tell Apollo Client where to look for the cached `Book` object, we can define a field policy `read` function for the `book` field:

```js{9-14}
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
book {
read(_, { args, toReference }) {
return toReference({
__typename: 'Book',
id: args.id,
});
}
}
}
}
}
}
});
```

This `read` function uses the `toReference` helper utility to generate and return a **cache reference** for a `Book` object, based on its `__typename` and `id`.

Now whenever a query includes the `book` field, the `read` function above executes and returns a reference to a `Book` object. Apollo Client uses this reference to look up the object in its cache and return it if it's present. If it _isn't_ present, Apollo Client knows it needs to execute the query over the network.

> ⚠️ **Note:** To avoid a network request, _all_ of a query's requested fields must already be present in the cache. If the detail view's query fetches _any_ `Book` field that the list view's query _didn't_, Apollo Client considers the cache hit to be incomplete, and it executes the full query over the network.
## Pagination utilities

### Incremental loading: `fetchMore`

`fetchMore` can be used to update the result of a query based on the data returned by another query. Most often, it is used to handle infinite-scroll pagination or other situations where you are loading more data when you already have some.

Expand Down Expand Up @@ -182,7 +242,7 @@ Here, the `fetchMore` query is the same as the query associated with the compone

Although `fetchMore` is often used for pagination, there are many other cases in which it is applicable. For example, suppose you have a list of items (say, a collaborative todo list) and you have a way to fetch items that have been updated after a certain time. Then, you don't have to refetch the whole todo list to get updates: you can just incorporate the newly added items with `fetchMore`, as long as your `updateQuery` function correctly merges the new results.

## The `@connection` directive
### The `@connection` directive

Fundamentally, paginated queries are the same as any other query with the exception that calls to `fetchMore` update the same cache key. Since these queries are cached by both the initial query and their parameters, a problem arises when later retrieving or updating paginated queries in the cache. We don’t care about pagination arguments such as limits, offsets, or cursors outside of the need to `fetchMore`, nor do we want to provide them simply for accessing cached data.

Expand Down Expand Up @@ -223,60 +283,3 @@ client.writeQuery({
```

Note that because we are only using the `type` argument in the store key, we don't have to provide `offset` or `limit`.

## Cache redirects using field policy `read` functions

> ⚠️ **Note:** Apollo Client >= 3.0 no longer supports the `ApolloClient` `cacheRedirects` constructor option. Equivalent `cacheRedirects` functionality can now be handled with field policy `read` functions, and is explained below.
In some cases, a query requests data that already exists in the cache under a different reference. A very common example of this is when your UI has a list view and a detail view that both use the same data. The list view might run the following query:

```graphql
query Books {
books {
id
title
abstract
}
}
```

When a specific book is selected, the detail view displays an individual item using this query:

```graphql
query Book($id: ID!) {
book(id: $id) {
id
title
abstract
}
}
```

We know that the data is most likely already in the client cache, but because it was requested with a different query, Apollo Client doesn't know that. To tell Apollo Client where to look for the existing `book` data, we can define a field policy `read` function for the `book` field:

```js
import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
book(_, { args, toReference }) {
return toReference({
__typename: 'Book',
id: args.id,
});
}
}
}
}
}
});
```
Now whenever a query includes the `book` field, the `read` function above executes and returns a reference that points to the book entity that was added to the cache when the `Books` list view query ran. Apollo Client uses the reference returned by the `read` function to look up the item in its cache.
The `toReference` helper utility is passed into `read` functions as part of the second parameter options object. It's used to generate an entity reference based on its `__typename` and `id`.
> ⚠️ **Note:** For the above to work properly, the data returned by the list query must include _all_ of the data that the specific detail query needs. If the specific detail query fetches a field that the list query doesn't return, Apollo Client considers the cache hit to be incomplete, and it attempts to fetch the full data set over the network (if network requests are enabled).
16 changes: 11 additions & 5 deletions docs/source/local-state/client-side-schema.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
---
title: Client-side schema
description: Configure a client-side schema with Apollo Client
description: Extend your schema with client-specific fields
---

You can optionally set a client-side schema to be used with Apollo Client, through the `ApolloClient` constructor `typeDefs` parameter. Your schema should be written in [Schema Definition Language](https://www.apollographql.com/docs/graphql-tools/generate-schema#schema-language). This schema is not used for validation like it is on the server because the `graphql-js` modules for schema validation would dramatically increase your bundle size. Instead, your client-side schema is used for introspection in [Apollo Client Devtools](https://github.com/apollographql/apollo-client-devtools), where you can explore your schema in GraphiQL.
You can optionally provide a **client-side schema** to Apollo Client that defines [local-only types and fields](./managing-state-with-field-policies/). You can define completely new types, or extend types from your server's schema with new fields.

The following demonstrates how to configure a client-side schema through the `ApolloClient` constructor:
As with any GraphQL schema, your client-side schema must be written in [Schema Definition Language](https://www.apollographql.com/docs/apollo-server/schema/schema/#the-schema-definition-language).

The client-side schema is _not_ used to validate operations like it is on the server (the `graphql-js` modules for schema validation would dramatically increase your bundle size). Instead, your client-side schema is used for introspection in the [Apollo Client Devtools](../development-testing/developer-tooling/#apollo-client-devtools), where you can explore your schema in GraphiQL.

## Setup

The following demonstrates how to define a client-side schema and provide it to the `ApolloClient` constructor:

```js
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
Expand All @@ -32,6 +38,6 @@ const client = new ApolloClient({
});
```

If you open up Apollo Client Devtools and click on the `GraphiQL` tab, you'll be able to explore your client schema in the "Docs" section. This example doesn't include a remote schema, but if it did, you would be able to see your local queries and mutations alongside your remote ones.
If you open up the [Apollo Client Devtools](../development-testing/developer-tooling/#apollo-client-devtools) and click on the `GraphiQL` tab, you'll be able to explore your client schema in the "Docs" section. This example doesn't include a remote schema, but if it did, you would be able to see your local queries and mutations alongside your remote ones.

![GraphiQL Console](../assets/client-schema.png)
<img class="screenshot" alt="GraphiQL in Apollo Devtools" src="../assets/client-schema.png"/>
37 changes: 26 additions & 11 deletions docs/source/performance/performance.mdx
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
---
title: Improving performance
title: Improving performance in Apollo Client
sidebar_title: Improving performance
---

import {
ExpansionPanel,
} from 'gatsby-theme-apollo-docs/src/components/expansion-panel';

## Redirecting to cached data

In some cases, a query requests data that already exists in the client cache under a different reference. A very common example of this is when your UI has a list view and a detail view that both use the same data. To avoid re-requesting data that already exists in the cache, see [Cache redirects using field policy `read` functions](../caching/advanced-topics#cache-redirects-using-field-policy-read-functions).
In some cases, a query might request data that's already present in the Apollo Client cache thanks to a _different_ query that already ran. For example, your UI might have both a list view and a detail view with queries that fetch the same fields from a particular object.

In cases like these, you can avoid sending your server a followup query that fetches identical data. To learn how, see [Cache redirects](../caching/advanced-topics#cache-redirects).

## Prefetching data

Prefetching is one of the easiest ways to make your application's UI feel a lot faster with Apollo Client. Prefetching simply means loading data into the cache before it needs to be rendered on the screen. Essentially, we want to load all data required for a view as soon as we can guess that a user will navigate to it.
Prefetching involves executing queries for data _before_ that data needs to be rendered. It helps your application's UI feel more responsive to the user.

Most of the time, prefetching involves querying for data as soon as you can guess that a user will _probably_ need it.

We can accomplish this in only a few lines of code by calling `client.query` whenever the user hovers over a link.
For example, this code snippet calls `client.query` to execute a query when the user hovers over a particular link (to a page that uses the data returned by the query):

```jsx
<ExpansionPanel title="Click to expand">

```jsx{19-24}
function Feed() {
const { loading, error, data, client } = useQuery(GET_DOGS);
Expand Down Expand Up @@ -55,12 +66,16 @@ function Feed() {
}
```

All we have to do is access the client in the render prop function and call `client.query` when the user hovers over the link. Once the user clicks on the link, the data will already be available in the Apollo cache, so the user won't see a loading state.
</ExpansionPanel>

When the `GET_DOG` query completes, its result is stored in the Apollo Client cache. This means that if the user then clicks the link, the dog's detail page can immediately populate that data from the cache, which feels instantaneous to the user.

In addition to a mouse hover, here are some other suggestions for situations when prefetching can be helpful:

There are a lot of different ways to anticipate that the user will end up needing some data in the UI. In addition to using the hover state, here are some other places you can preload data:
* During a multi-step flow (such as a wizard), you can preload each _next_ step's data during each _current_ step.
* If your app's analytics indicate a frequent transition between two particular views, you can use prefetching to optimize for that path.
* If a region of a page has multiple tabs or slides (such as a carousel), you can preload data for some or all of them to make transitions feel snappier.

1. The next step of a multi-step wizard immediately
2. The route of a call-to-action button
3. All of the data for a sub-area of the application, to make navigating within that area instant
A special form of prefetching is [store hydration from the server](./server-side-rendering/#rehydrating-the-client-side-cache), so you might also consider hydrating more data than is actually needed for the first page load to make other interactions faster.

If you have some other ideas, please send a PR to this article, and maybe add some more code snippets. A special form of prefetching is [store hydration from the server](server-side-rendering/#rehydrating-the-client-side-cache), so you might also consider hydrating more data than is actually needed for the first page load to make other interactions faster.
Feel free to submit a PR with suggestions for other preloading opportunities!

0 comments on commit bd03ff0

Please sign in to comment.