Docs
Launch GraphOS Studio
You're viewing documentation for a previous version of this software. Switch to the latest stable version.

Queries

Learn how to fetch data with the useQuery hook


Fetching data in a simple, predictable way is one of the core features of . This article demonstrates how to fetch data in React with the useQuery hook and attach the result to your UI. You'll also learn how simplifies data management code by tracking error and loading states for you.

Prerequisites

This article assumes you're familiar with building basic queries. If you need a refresher, we recommend that you

and
practice running queries in GraphiQL
. queries are standard , so any that runs in GraphiQL will also run when provided to useQuery.

This article also assumes that you've already set up and have wrapped your React app in an ApolloProvider component. Read our

if you need help with either of those steps.

To follow along with the examples below, open up our

and
sample GraphQL server
on CodeSandbox. You can view the completed version of the app
here
.

Executing a query

The useQuery

is the primary API for executing queries in an Apollo application. To run a within a React component, call useQuery and pass it a string. When your component renders, useQuery returns an object from that contains loading, error, and data properties you can use to render your UI.

Let's look at an example. First, we'll create a named GET_DOGS. Remember to wrap strings in the gql function to parse them into documents:

index.js
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
const GET_DOGS = gql`
{
dogs {
id
breed
}
}
`;

Next, we'll create a component named Dogs. Inside it, we'll pass our GET_DOGS to the useQuery hook:

index.js
const Dogs = ({ onDogSelected }) => (
<Query query={GET_DOGS}>
{({ loading, error, data }) => {
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return (
<select name="dog" onChange={onDogSelected}>
{data.dogs.map(dog => (
<option key={dog.id} value={dog.breed}>
{dog.breed}
</option>
))}
</select>
);
}}
</Query>
);

As our executes and the values of loading, error, and data change, the Dogs component can intelligently render different UI elements according to the 's state:

  • As long as loading is true (indicating the is still in flight), the component presents a Loading... notice.
  • When loading is false and there is no error, the has completed. The component renders a dropdown menu that's populated with the list of dog breeds returned by the server.

When the user selects a dog breed from the populated dropdown, the selection is sent to the parent component via the provided onDogSelected function.

In the next step, we'll associate the dropdown with a more sophisticated that uses .

Caching query results

Whenever fetches results from your server, it automatically caches those results locally. This makes subsequent executions of the same extremely fast.

To see this caching in action, let's build a new component called DogPhoto. DogPhoto accepts a prop called breed that reflects the current value of the dropdown menu in our Dogs component:

index.js
const GET_DOG_PHOTO = gql`
query Dog($breed: String!) {
dog(breed: $breed) {
id
displayImage
}
}
`;
const DogPhoto = ({ breed }) => (
<Query query={GET_DOG_PHOTO} variables={{ breed }}>
{({ loading, error, data }) => {
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
);
}}
</Query>
);

Notice that we're providing a configuration option (variables) to the useQuery hook this time. The variables option is an object that contains all of the we want to pass to our . In this case, we want to pass the currently selected breed from the dropdown.

Select bulldog from the dropdown to see its photo appear. Then switch to another breed, and then switch back to bulldog. You'll notice that the bulldog photo loads instantly the second time around. This is the Apollo cache at work!

Next, let's learn some techniques for ensuring that our cached data is fresh.

Updating cached query results

Caching results is handy and easy to do, but sometimes you want to make sure that cached data is up to date with your server. supports two strategies for this: polling and refetching.

Polling

Polling provides near-real-time synchronization with your server by causing a to execute periodically at a specified interval. To enable polling for a query, pass a pollInterval configuration option to the useQuery hook with an interval in milliseconds:

index.js
const DogPhoto = ({ breed }) => (
<Query
query={GET_DOG_PHOTO}
variables={{ breed }}
skip={!breed}
pollInterval={500}
>
{({ loading, error, data }) => {
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
);
}}
</Query>
);

By setting the pollInterval to 500, you'll fetch the current breed's image from the server every 0.5 seconds. Note that if you set pollInterval to 0, the will not poll.

You can also start and stop polling dynamically with the startPolling and stopPolling functions that are returned by the

.

Refetching

Refetching enables you to refresh results in response to a particular user action, as opposed to using a fixed interval.

Let's add a button to our DogPhoto component that calls our 's refetch function whenever it's clicked.

You can optionally provide a new variables object to the refetch function. If you don't (as is the case in the following example), the uses the same that it used in its previous execution.

index.js
const DogPhoto = ({ breed }) => (
<Query query={GET_DOG_PHOTO} variables={{ breed }} skip={!breed}>
{({ loading, error, data, refetch }) => {
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<div>
<img
src={data.dog.displayImage}
style={{ height: 100, width: 100 }}
/>
<button onClick={() => refetch()}>Refetch!</button>
</div>
);
}}
</Query>
);

Click the button and notice that the UI updates with a new dog photo. Refetching is an excellent way to guarantee fresh data, but it introduces some complexity with loading state. In the next section, we'll cover strategies for handling complex loading and error state.

Inspecting loading states

We've already seen that the useQuery hook exposes our 's current loading state. This is helpful when a query first loads, but what happens to our loading state when we're refetching or polling?

Let's return to our refetching example from the previous section. If you click the refetch button, you'll see that the component doesn't re-render until the new data arrives. What if we want to indicate to the user that we're refetching the photo?

Luckily, the useQuery hook's result object provides fine-grained information about the status of the via the networkStatus property. To take advantage of this information, we need to set the notifyOnNetworkStatusChange option to true so our component re-renders while a refetch is in flight:

index.js
const DogPhoto = ({ breed }) => (
<Query
query={GET_DOG_PHOTO}
variables={{ breed }}
skip={!breed}
notifyOnNetworkStatusChange
>
{({ loading, error, data, refetch, networkStatus }) => {
if (networkStatus === 4) return 'Refetching!';
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<div>
<img
src={data.dog.displayImage}
style={{ height: 100, width: 100 }}
/>
<button onClick={() => refetch()}>Refetch!</button>
</div>
);
}}
</Query>
);

The networkStatus property is an enum with number values from 1 to 8 that represent different loading states. 4 corresponds to a refetch, but there are also values for polling and pagination. For a full list of all the possible loading states, check out the

.

Inspecting error states

You can customize your error handling by providing the errorPolicy configuration option to the useQuery hook. The default value is none, which tells to treat all errors as runtime errors. In this case, Apollo Client discards any response data returned by the server and sets the error property in the useQuery result object to true.

If you set errorPolicy to all, useQuery does not discard response data, allowing you to render partial results.

Executing queries manually

When React mounts and renders a component that calls the useQuery hook, automatically executes the specified . But what if you want to execute a query in response to a different event, such as a user clicking a button?

The useLazyQuery hook is perfect for executing queries in response to events other than component rendering. This hook acts just like useQuery, with one key exception: when useLazyQuery is called, it does not immediately execute its associated . Instead, it returns a function in its result tuple that you can call whenever you're ready to execute the query:

index.js
import React, { useState } from 'react';
import { useLazyQuery } from '@apollo/react-hooks';
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (data && data.dog) {
setDog(data.dog);
}
return (
<div>
{dog && <img src={dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
Click me!
</button>
</div>
);
}

To view a complete version of the app we just built, check out the CodeSandbox

.

useQuery API

Supported options and result for the useQuery hook are listed below.

Most calls to useQuery can omit the majority of these options, but it's useful to know they exist. To learn about the useQuery hook API in more detail with usage examples, see the

.

Options

The useQuery hook accepts the following options:

OptionTypeDescription
queryDocumentNodeA GraphQL query document parsed into an AST by graphql-tag. Optional for the useQuery Hook since the query can be passed in as the first parameter to the Hook. Required for the Query component.
variables{ [key: string]: any }An object containing all of the variables your query needs to execute
pollIntervalnumberSpecifies the interval in ms at which you want your component to poll for data. Defaults to 0 (no polling).
notifyOnNetworkStatusChangebooleanWhether updates to the network status or network error should re-render your component. Defaults to false.
fetchPolicyFetchPolicyHow you want your component to interact with the Apollo cache. Defaults to "cache-first".
errorPolicyErrorPolicyHow you want your component to handle network and GraphQL errors. Defaults to "none", which means we treat GraphQL errors as runtime errors.
ssrbooleanPass in false to skip your query during server-side rendering.
displayNamestringThe name of your component to be displayed in React DevTools. Defaults to 'Query'.
skipbooleanIf skip is true, the query will be skipped entirely. Not available with useLazyQuery.
onCompleted(data: TData | {}) => voidA callback executed once your query successfully completes.
onError(error: ApolloError) => voidA callback executed in the event of an error.
contextRecord<string, any>Shared context between your component and your network interface (Apollo Link). Useful for setting headers from props or sending information to the request function of Apollo Boost.
partialRefetchbooleanIf true, perform a query refetch if the query result is marked as being partial, and the returned data is reset to an empty Object by the Apollo Client QueryManager (due to a cache miss). The default value is false for backwards-compatibility's sake, but should be changed to true for most use-cases.
clientApolloClientAn ApolloClient instance. By default useQuery / Query uses the client passed down via context, but a different client can be passed in.
returnPartialDatabooleanOpt into receiving partial results from the cache for queries that are not fully satisfied by the cache. false by default.

Result

After being called, the useQuery hook returns a result object with the following properties. This object contains your result, plus some helpful functions for refetching, dynamic polling, and pagination.

PropertyTypeDescription
dataTDataAn object containing the result of your GraphQL query. Defaults to undefined.
loadingbooleanA boolean that indicates whether the request is in flight
errorApolloErrorA runtime error with graphQLErrors and networkError properties
variables{ [key: string]: any }An object containing the variables the query was called with
networkStatusNetworkStatusA number from 1-8 corresponding to the detailed state of your network request. Includes information about refetching and polling status. Used in conjunction with the notifyOnNetworkStatusChange prop.
refetch(variables?: TVariables) => Promise<ApolloQueryResult>A function that allows you to refetch the query and optionally pass in new variables
fetchMore({ query?: DocumentNode, variables?: TVariables, updateQuery: Function}) => Promise<ApolloQueryResult>A function that enables
pagination
for your query
startPolling(interval: number) => voidThis function sets up an interval in ms and fetches the query each time the specified interval passes.
stopPolling() => voidThis function stops the query from polling.
subscribeToMore(options: { document: DocumentNode, variables?: TVariables, updateQuery?: Function, onError?: Function}) => () => voidA function that sets up a
subscription
. subscribeToMore returns a function that you can use to unsubscribe.
updateQuery(previousResult: TData, options: { variables: TVariables }) => TDataA function that allows you to update the query's result in the cache outside the context of a fetch, mutation, or subscription
clientApolloClientYour ApolloClient instance. Useful for manually firing queries or writing data to the cache.
calledbooleanA boolean indicating if the query function has been called, used by useLazyQuery (not set for useQuery / Query).

Next steps

Now that you understand how to fetch data with the useQuery hook,

After that, learn about some other handy features:

  • Local state management
    : Learn how to local data.
  • Pagination
    : Building lists has never been easier thanks to 's fetchMore function. Learn more in our pagination tutorial.
Previous
Get started
Next
Mutations
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company