Skip to content

Commit 5febf04

Browse files
committed
Add more robust section for useLazyQuery
1 parent 5439863 commit 5febf04

1 file changed

Lines changed: 271 additions & 1 deletion

File tree

docs/source/data/queries.mdx

Lines changed: 271 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,277 @@ function GetDogsOnClick() {
281281
282282
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.
283283
284-
For a full list of supported options, see the [API reference](../api/react/useLazyQuery).
284+
### Re-rendering with new options
285+
286+
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.
287+
288+
The following is an example that changes the fetch policy depending on whether the user is online or offline:
289+
290+
```tsx
291+
function GetDogs({ isOnline }) {
292+
const [getDogs, { loading, data }] = useLazyQuery(GET_DOGS, {
293+
fetchPolicy: isOnline ? "network-only" : "cache-only",
294+
});
295+
296+
if (loading) return <p>Loading ...</p>;
297+
298+
return (
299+
<div>
300+
{data && <Dogs data={data.dogs} />}
301+
<button onClick={() => getDogs()}>Get dogs</button>
302+
</div>
303+
);
304+
}
305+
```
306+
307+
<Note>
308+
309+
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` will return the updated options. This means the updated options will be used for other APIs (e.g. such as `refetch`), even before calling the execution function again.
310+
311+
</Note>
312+
313+
### Working with variables
314+
315+
You provide `variables` to the execution function when executing the query.
316+
317+
The following is an example that gets a specific dog's photo when clicking the "Get photo" button:
318+
319+
```jsx
320+
function DogPhoto() {
321+
const [getDogPhoto, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);
322+
323+
if (loading) return <p>Loading ...</p>;
324+
if (error) return `Error! ${error.message}`;
325+
326+
return (
327+
<div>
328+
{data?.dog && <img src={data.dog.displayImage} />}
329+
<button onClick={() => getDogPhoto({ variables: { name: "Lucky" } })}>
330+
Get photo
331+
</button>
332+
</div>
333+
);
334+
}
335+
```
336+
337+
<Note>
338+
339+
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 will see a TypeScript error.
340+
341+
</Note>
342+
343+
#### Changing variables
344+
345+
You change variables by calling the execution function with updated variables.
346+
347+
The following is an example that get's the selected dog's photo when clicking the "Get photo" button.
348+
349+
```jsx
350+
function DogPhoto() {
351+
const [selectedDog, setSelectedDog] = useState(null);
352+
const [getDogPhoto, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);
353+
354+
if (loading) return <p>Loading ...</p>;
355+
if (error) return `Error! ${error.message}`;
356+
357+
return (
358+
<div>
359+
<DogSelect
360+
onChange={(dogName) => setSelectedDog(dogName)}
361+
value={selectedDog}
362+
/>
363+
{data?.dog && <img src={data.dog.displayImage} />}
364+
{selectedDog && (
365+
<button
366+
onClick={() => getDogPhoto({ variables: { name: selectedDog } })}
367+
>
368+
Get photo
369+
</button>
370+
)}
371+
</div>
372+
);
373+
}
374+
```
375+
376+
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 most recently used variables provided from the last execution of the query.
377+
378+
<Tip>
379+
380+
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.
381+
382+
</Tip>
383+
384+
```jsx
385+
function DogPhoto() {
386+
const [getDogPhoto, { called, variables }] = useLazyQuery(GET_DOG_PHOTO);
387+
388+
return (
389+
<div>
390+
{/* ... */}
391+
<button onClick={() => getDogPhoto({ variables: { name: selectedDog } })}>
392+
Get photo
393+
</button>
394+
395+
{called && <span>Last fetched: {variables.name}</span>}
396+
</div>
397+
);
398+
}
399+
```
400+
401+
### Using the promise returned from the execution function
402+
403+
The execution function returns a promise that resolves with the query result:
404+
405+
```jsx
406+
function GetDogs() {
407+
const [getDogs, { data, loading, error }] = useLazyQuery(GET_DOGS);
408+
409+
const handleClick = async () => {
410+
const { data } = await getDogs();
411+
412+
// Do something with `data`
413+
};
414+
415+
return (
416+
<div>
417+
{/* ... */}
418+
<button onClick={handleClick}>Get dogs</button>
419+
</div>
420+
);
421+
}
422+
```
423+
424+
The result returned from the promise is useful when you need to execute side-effects using the data returned by the query.
425+
426+
<Tip>
427+
428+
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 is kept up-to-date with cache changes as they occur throughout your application.<br /><br />
429+
430+
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.
431+
432+
<ExpansionPanel title="Example">
433+
434+
```ts
435+
import { useApolloClient } from "@apollo/client/react";
436+
437+
function GetDogs() {
438+
const client = useApolloClient();
439+
440+
const handleClick = async () => {
441+
const { data } = await client.query({ query: GET_DOGS });
442+
443+
// Do something with `data`
444+
};
445+
446+
return <button onClick={handleClick}>Get dogs</button>;
447+
}
448+
```
449+
450+
</ExpansionPanel>
451+
452+
</Tip>
453+
454+
#### Handling errors
455+
456+
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).
457+
458+
##### `errorPolicy: "none"`
459+
460+
The promise rejects with the error that caused the query to fail.
461+
462+
```ts
463+
const handleClick = async () => {
464+
try {
465+
const { data } = await getDogs();
466+
} catch (error) {
467+
if (CombinedGraphQLErrors.is(error)) {
468+
// handle GraphQL errors
469+
}
470+
471+
// Handle other error types
472+
console.log(error.message);
473+
}
474+
};
475+
```
476+
477+
<Note>
478+
479+
Errors always cause the promise to reject. The `error` property is never set when the promise resolves.
480+
481+
</Note>
482+
483+
##### `errorPolicy: "all"`
484+
485+
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.
486+
487+
```ts
488+
const handleClick = async () => {
489+
const { data, error } = await getDogs();
490+
491+
if (error && CombinedGraphQLErrors.is(error)) {
492+
// handle GraphQL errors returned by the query
493+
}
494+
};
495+
```
496+
497+
<Note>
498+
499+
`data` might not contain any partial data will instead be set as `undefined`. This typically occurs when a [network error](./error-handling#network-errors) causes the query to fail since the error might not be associated with GraphQL execution.
500+
501+
</Note>
502+
503+
##### `errorPolicy: "ignore"`
504+
505+
The promise resolves with an object that includes any partial data returned by the query. Errors are discarded.
506+
507+
```ts
508+
const handleClick = async () => {
509+
const { data } = await getDogs();
510+
511+
if (data !== undefined) {
512+
// Do something with the returned data
513+
}
514+
};
515+
```
516+
517+
<Tip>
518+
519+
`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.
520+
521+
</Tip>
522+
523+
#### Retaining query results
524+
525+
In-flight queries executed by `useLazyQuery` are aborted when the component unmounts, causing the promise to reject. In some cases, you may find this behavior undesirable and would prefer to let the query run to completion.
526+
527+
The promise returned by the execution function includes a `.retain()` method. When called, it ensures the query continues running even when the component unmounts.
528+
529+
```jsx
530+
function GetDogs() {
531+
const [getDogs] = useLazyQuery(GET_DOGS);
532+
533+
const handleClick = async () => {
534+
const promise = getDogs();
535+
536+
// Retain the query even if component unmounts
537+
promise.retain();
538+
539+
const { data } = await promise;
540+
541+
// Do something with data
542+
};
543+
544+
return <button onClick={handleClick}>Get dogs</button>;
545+
}
546+
```
547+
548+
The `retain()` method returns the original promise. The previous example can be shortened to a single line:
549+
550+
```ts
551+
const { data } = await getDogs().retain();
552+
```
553+
554+
For a complete list of supported options and result properties, see the [API reference](../api/react/useLazyQuery).
285555
286556
## Setting a fetch policy
287557

0 commit comments

Comments
 (0)