Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 281 additions & 11 deletions docs/source/data/queries.mdx

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note: I've avoided using dataState in the examples until we have that completely flushed out.

Original file line number Diff line number Diff line change
Expand Up @@ -250,38 +250,308 @@

For more information, see [Handling operation errors](./error-handling/).

## Manual execution with `useLazyQuery`
## Fetching in response to user interaction

When React renders a component that calls `useQuery`, Apollo Client _automatically_ executes the corresponding query. But what if you want to execute a query in response to a different event, such as a user clicking a button?
When React renders a component that calls `useQuery`, Apollo Client automatically executes the corresponding query. But what if you want to execute a query in response to a user interaction, such as a user clicking a button?

The `useLazyQuery` hook is perfect for executing queries in response to events besides component rendering. Unlike with `useQuery`, when you call `useLazyQuery`, it does _not_ immediately execute its associated query. Instead, it returns a **query function** in its result tuple that you call whenever you're ready to execute the query.
The [`useLazyQuery`](../api/react/useLazyQuery) hook is suited for manually executing queries. Unlike `useQuery`, when you use `useLazyQuery`, it does not immediately execute its associated query. Instead, it returns an execution function that you call whenever you need to execute the query.

Check notice on line 257 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L257

The original phrasing is passive. This change uses more encouraging language ('ideal'), a contraction ('doesn't'), and simplifies the final clause for better readability and a more approachable tone. ```suggestion The [<code>useLazyQuery</code>](../api/react/useLazyQuery) hook is ideal for manually executing queries. Unlike <code>useQuery</code>, <code>useLazyQuery</code> doesn't immediately execute its associated query. Instead, it returns a function that you call to execute the query. ```

Here's an example:

```jsx {2,5,13} title="index.js"
```jsx {2,5,15} title="index.js"
import React from "react";
import { useLazyQuery } from "@apollo/client/react";

function DelayedQuery() {
const [getDog, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);
function GetDogsOnClick() {
const [getDogs, { loading, error, data }] = useLazyQuery(GET_DOGS);

if (loading) return <p>Loading ...</p>;
if (error) return `Error! ${error}`;
if (error) return `Error! ${error.message}`;

return (
<div>
{data?.dogs.map((dog) => (
<Dog key={dog.id} dog={dog} />
))}
<button onClick={() => getDogs()}>Load dogs</button>
</div>
);
}
```

The first item in `useLazyQuery`'s return tuple is the execution function, and the second item is an object that contains information about the executed query, such as the `loading`, `error`, `data`, and `dataState` properties.

Check warning on line 282 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L282

The original sentence is long and uses jargon ('tuple'). This revision simplifies the sentence structure for clarity and removes the `dataState` property, which is not part of the returned object. ```suggestion The <code>useLazyQuery</code> hook returns a tuple where the first item is the execution function. The second item is an object containing the query's result, with properties like <code>loading</code>, <code>error</code>, and <code>data</code>. ```

### Re-rendering with new options

Unlike `useQuery`, options provided to `useLazyQuery` that change on re-renders do not automatically execute the query. Instead, `useLazyQuery` waits to execute the query using the updated options until the execution function is called again.

Check notice on line 286 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L286

This change reframes the sentence to be more reader-centric ('if you provide') and uses a contraction ('doesn't') to align with the approachable voice. ```suggestion Unlike <code>useQuery</code>, if you provide options to <code>useLazyQuery</code> that change on re-renders, the hook doesn't automatically execute the query. Instead, <code>useLazyQuery</code> waits for you to call the execution function again before running the query with the updated options. ```

The following is an example that changes the fetch policy depending on whether the user is online or offline:

```tsx
function GetDogs({ isOnline }) {
const [getDogs, { loading, data }] = useLazyQuery(GET_DOGS, {
fetchPolicy: isOnline ? "network-only" : "cache-only",
});

if (loading) return <p>Loading ...</p>;

return (
<div>
{data && <Dogs data={data.dogs} />}
<button onClick={() => getDogs()}>Get dogs</button>
</div>
);
}
```

<Note>

The changed options are immediately applied to the underlying `ObservableQuery` (accessible by the `observable` property) even though the query is not executed. Inspecting the options on the `observable` returns the updated options. This means the updated options will be used for other APIs (such as `refetch`), even before calling the execution function again.

Check notice on line 309 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L309

This revision uses contractions ('isn't'), favors the present tense ('use' instead of 'will be used'), and simplifies the sentence structure for better flow and readability. ```suggestion The changed options are immediately applied to the underlying <code>ObservableQuery</code> (accessible via the <code>observable</code> property), even though the query isn't executed. This means other APIs, such as <code>refetch</code>, use the updated options even before you call the execution function again. ```

</Note>

### Working with variables

You provide `variables` to the execution function when executing the query.

The following is an example that gets a specific dog's photo when clicking the "Get photo" button:

Check warning on line 317 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L317

Button names and other interactive UI elements should be formatted with bold. ```suggestion The following example gets a specific dog's photo when you click the **Get photo** button: ```

```jsx
function DogPhoto() {
const [getDogPhoto, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);

if (loading) return <p>Loading ...</p>;
if (error) return `Error! ${error.message}`;

return (
<div>
{data?.dog && <img src={data.dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: "bulldog" } })}>
Click me!
<button onClick={() => getDogPhoto({ variables: { name: "Lucky" } })}>
Get photo
</button>
</div>
);
}
```

The first item in `useLazyQuery`'s return tuple is the query function, and the second item is the same result object returned by `useQuery`.
<Note>

When using TypeScript, the `variables` option is required along with any required variables when the query provided to `useLazyQuery` contains required variables. If the options argument is not provided to the execution function, or the `variables` option is missing required variables, you see a TypeScript error.

Check warning on line 339 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L339

The original sentence is repetitive and difficult to parse. This change simplifies the structure to make the requirement clearer. ```suggestion When using TypeScript, if the query you provide to <code>useLazyQuery</code> has required variables, you must pass them in the <code>variables</code> option to the execution function. If you don't provide this options argument, or if the <code>variables</code> option is missing required variables, you'll see a TypeScript error. ```

</Note>

#### Changing variables

You change variables by calling the execution function with updated variables.

Check notice on line 345 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L345

This phrasing is more direct and action-oriented, which is helpful for instructional content. ```suggestion To change variables, call the execution function again with the new variables. ```

The following is an example that gets the selected dog's photo when clicking the "Get photo" button.

Check warning on line 347 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L347

Button names and other interactive UI elements should be formatted with bold. ```suggestion The following example gets the selected dog's photo when you click the **Get photo** button. ```

```jsx
function DogPhoto() {
const [selectedDog, setSelectedDog] = useState(null);
const [getDogPhoto, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);

if (loading) return <p>Loading ...</p>;
if (error) return `Error! ${error.message}`;

return (
<div>
<DogSelect
onChange={(dogName) => setSelectedDog(dogName)}
value={selectedDog}
/>
{data?.dog && <img src={data.dog.displayImage} />}
{selectedDog && (
<button
onClick={() => getDogPhoto({ variables: { name: selectedDog } })}
>
Get photo
</button>
)}
</div>
);
}
```

If you need to reference the currently executed query's variables, use the `variables` property returned by `useLazyQuery`. The `variables` property contains the value of the variables from the last execution of the query.

<Tip>

The `variables` property is empty until the execution function is called for the first time. Use the `called` property returned by `useLazyQuery` to determine when the execution function has been called at least once.

Check notice on line 380 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L380

This revision is more reader-centric ('until you call') and avoids repeating the word 'called' for better readability. ```suggestion The <code>variables</code> property is empty until you call the execution function for the first time. Use the <code>called</code> property from <code>useLazyQuery</code> to check if the execution function has run at least once. ```
Comment thread
jerelmiller marked this conversation as resolved.
Outdated

</Tip>

```jsx
function DogPhoto() {
const [getDogPhoto, { called, variables }] = useLazyQuery(GET_DOG_PHOTO);

return (
<div>
{/* ... */}
<button onClick={() => getDogPhoto({ variables: { name: selectedDog } })}>
Get photo
</button>

{called && <span>Last fetched: {variables.name}</span>}
</div>
);
}
```

### Using the promise returned from the execution function

The execution function returns a promise that resolves with the query result:

```jsx
function GetDogs() {
const [getDogs, { data, loading, error }] = useLazyQuery(GET_DOGS);

const handleClick = async () => {
const { data } = await getDogs();

// Do something with `data`
};

return (
<div>
{/* ... */}
<button onClick={handleClick}>Get dogs</button>
</div>
);
}
```

The result returned from the promise is useful when you need to execute side-effects using the data returned by the query.

<Tip>

Use the `data`, `error`, and other properties returned by `useLazyQuery` to sync the query state with your component. Avoid using your own state setters from React's `useState` hook with data resolved from the promise. This ensures your component stays up-to-date with cache changes as they occur throughout your application.<br /><br />

In cases where you don't need to keep your component in sync with query state, use `client.query()` directly because it won't unnecessarily render your component for data that you don't use.

Check notice on line 430 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L430

The original sentence ending is slightly awkward. This change splits the sentence and rephrases the second part for improved clarity and flow. ```suggestion In cases where you don't need to keep your component in sync with query state, use <code>client.query()</code> directly. This approach avoids unnecessary re-renders for data your component doesn't use. ```

<ExpansionPanel title="Example">
Comment thread
jerelmiller marked this conversation as resolved.

```ts
import { useApolloClient } from "@apollo/client/react";

function GetDogs() {
const client = useApolloClient();

const handleClick = async () => {
const { data } = await client.query({ query: GET_DOGS });

// Do something with `data`
};

return <button onClick={handleClick}>Get dogs</button>;
}
```

</ExpansionPanel>

</Tip>

#### Handling errors

The promise resolves or rejects depending on the configured [`errorPolicy`](./error-handling#setting-an-error-policy). For a more comprehensive guide on working with errors, read the [Error handling documentation](./error-handling).

##### `errorPolicy: "none"`

The promise rejects with the error that caused the query to fail.

```ts
const handleClick = async () => {
try {
const { data } = await getDogs();
} catch (error) {
if (CombinedGraphQLErrors.is(error)) {
// handle GraphQL errors
}

// Handle other error types
console.log(error.message);
}
};
```

<Note>

Errors always cause the promise to reject. The `error` property is never set when the promise resolves.

Check notice on line 479 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L479

Use contractions like 'isn't' to maintain an approachable tone. ```suggestion Errors always cause the promise to reject. The <code>error</code> property isn't set when the promise resolves. ```

</Note>

##### `errorPolicy: "all"`

The promise resolves with an object that includes the error and any partial data returned by the query. Read the partial data on the `data` property and the error that caused the query to fail on the `error` property.

Check notice on line 485 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L485

This revision is more reader-centric ('You can access') than the imperative 'Read', which is more suitable for explanatory text. ```suggestion The promise resolves with an object that includes the error and any partial data from the query. You can access the partial data from the <code>data</code> property and the error from the <code>error</code> property. ```

```ts
const handleClick = async () => {
const { data, error } = await getDogs();

if (error && CombinedGraphQLErrors.is(error)) {
// handle GraphQL errors returned by the query
}
};
```

<Note>

`data` might not contain any partial data and is instead set to `undefined`. This typically occurs when a [network error](./error-handling#network-errors) causes the query to fail because the error might not be associated with GraphQL execution.

Check notice on line 499 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L499

This change simplifies the phrasing ('is instead set to' -> 'is') and uses a contraction ('isn't') for better readability. ```suggestion <code>data</code> might be <code>undefined</code> instead of containing partial data. This typically happens when a [network error](./error-handling#network-errors) causes the query to fail, because the error isn't associated with GraphQL execution. ```

</Note>

##### `errorPolicy: "ignore"`

The promise resolves with an object that includes any partial data returned by the query. Errors are discarded.

```ts
const handleClick = async () => {
const { data } = await getDogs();

if (data !== undefined) {
// Do something with the returned data
}
};
```

<Tip>

`data` might be `undefined` in the event a [network error](./error-handling#network-errors) is raised. We recommend checking if `data` is `undefined` before attempting to use it in case an error occurs during query execution.

Check warning on line 519 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L519

This revision simplifies the phrasing ('in the event a...is raised' -> 'if a...occurs') and uses a direct, imperative instruction ('Check if...') instead of 'We recommend', which is more authoritative. ```suggestion <code>data</code> might be <code>undefined</code> if a [network error](./error-handling#network-errors) occurs. Check if <code>data</code> is <code>undefined</code> before using it to prevent issues if an error occurs during query execution. ```

</Tip>

#### Retaining query results

In-flight queries executed by `useLazyQuery` are aborted when the component unmounts, causing the promise to reject. In some cases, you might find this behavior undesirable and would prefer to let the query run to completion.

Check notice on line 525 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L525

The original phrasing is slightly weak ('might find this undesirable'). This revision is more direct and clearly states the default behavior and the possibility of changing it. ```suggestion By default, in-flight queries from <code>useLazyQuery</code> are aborted when their component unmounts, which rejects the promise. If you need the query to run to completion, you can change this behavior. ```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
In-flight queries executed by `useLazyQuery` are aborted when the component unmounts, causing the promise to reject. In some cases, you might find this behavior undesirable and would prefer to let the query run to completion.
In-flight queries executed by `useLazyQuery` are aborted when the component unmounts or another query ist started via the execution function, causing the promise to reject. (This is a silent rejection, so unless you called `.then`, `.catch` or `await`ed the promise, you won't see it.)

@phryneas phryneas Aug 8, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two independent edits in this - the addition in parantheses could also be a <Note pararaph? Not sure.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ya I think a note makes sense. I'll expand on this a bit more as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added in b2b37e6


The promise returned by the execution function includes a `.retain()` method. When called, it ensures the query continues running even when the component unmounts.

Check notice on line 527 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L527

Using an imperative verb ('Call this method') is more direct and instructive for the reader. ```suggestion The promise returned by the execution function includes a <code>.retain()</code> method. Call this method to ensure the query continues running even if the component unmounts. ```
Comment thread
jerelmiller marked this conversation as resolved.
Outdated

```jsx
function GetDogs() {
const [getDogs] = useLazyQuery(GET_DOGS);

const handleClick = async () => {
const promise = getDogs();

// Retain the query even if component unmounts
promise.retain();

const { data } = await promise;

// Do something with data
};

return <button onClick={handleClick}>Get dogs</button>;
}
```

The `retain()` method returns the original promise. The previous example can be shortened to a single line:

Check notice on line 548 in docs/source/data/queries.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/data/queries.mdx#L548

This change makes the sentence more reader-centric ('you can shorten') and improves the logical flow by adding 'so'. ```suggestion The <code>retain()</code> method returns the original promise, so you can shorten the previous example to a single line: ```

```ts
const { data } = await getDogs().retain();
```

For a full list of supported options, see the [API reference](../api/react/useLazyQuery).
For a complete list of supported options and result properties, see the [`useLazyQuery` API reference](../api/react/useLazyQuery).

## Setting a fetch policy

Expand Down
7 changes: 3 additions & 4 deletions docs/source/get-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ Applications that use Apollo Client require two top-level dependencies:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Above this line:

-Applications that use Apollo Client require two top-level dependencies:
+Applications that use Apollo Client require three top-level dependencies:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch! I changed it to "the following" instead so we don't have to remember to update the count ever again 🤣

- `@apollo/client`: This single package contains virtually everything you need to set up Apollo Client. It includes the in-memory cache, local state management, error handling, and a React-based view layer.
- `graphql`: This package provides logic for parsing GraphQL queries.
- `rxjs`: This package provides the `Observable` primitive used throughout Apollo Client.

Run the following command to install both of these packages:

```bash
npm install @apollo/client graphql
npm install @apollo/client graphql rxjs
```

> If you're using a React sandbox from CodeSandbox and you encounter a `TypeError`, try downgrading the version of the `graphql` package to `15.8.0` in the Dependencies panel. If you encounter a _different_ error after downgrading, refresh the page.

Our example application will use the [FlyBy GraphQL API](https://flyby-router-demo.herokuapp.com/) from Apollo Odyssey's [Voyage tutorial series](https://www.apollographql.com/tutorials/voyage-part1/). This API provides a list of intergalactic travel locations and details about those locations 👽

## Step 3: Initialize `ApolloClient`
Expand Down Expand Up @@ -78,7 +77,7 @@ client
.then((result) => console.log(result));
```

Run this code, open your console, and inspect the result object. You should see a `data` property with `locations` attached, along with some other properties like `loading` and `networkStatus`. Nice!
Run this code, open your console, and inspect the result object. You should see a `data` property with `locations` attached. Nice!

Although executing GraphQL operations directly like this can be useful, Apollo Client really shines when it's integrated with a view layer like React. You can bind queries to your UI and update it automatically as new data is fetched.

Expand Down