Queries
Learn how to fetch data with the useQuery hook
Fetching data in a simple, predictable way is one of the core features of Apollo Client. This article demonstrates how to fetch GraphQL data in React with the useQuery
hook and attach the result to your UI. You'll also learn how Apollo Client simplifies data management code by tracking error and loading states for you.
Prerequisites
This article assumes you're familiar with building basic GraphQL queries. If you need a refresher, we recommend that you read this guide and practice running queries in GraphiQL. Apollo Client queries are standard GraphQL, so any query that runs in GraphiQL will also run when provided to useQuery
.
This article also assumes that you've already set up Apollo Client and have wrapped your React app in an ApolloProvider
component. Read our getting started guide if you need help with either of those steps.
To follow along with the examples below, open up our starter project and sample GraphQL server on CodeSandbox. You can view the completed version of the app here.
Executing a query
The useQuery
React hook is the primary API for executing queries in an Apollo application. To run a query within a React component, call useQuery
and pass it a GraphQL query string. When your component renders, useQuery
returns an object from Apollo Client 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 GraphQL query named GET_DOGS
. Remember to wrap query strings in the gql
function to parse them into query documents:
1import gql from 'graphql-tag';
2import { useQuery } from '@apollo/react-hooks';
3
4const GET_DOGS = gql`
5 {
6 dogs {
7 id
8 breed
9 }
10 }
11`;
1import gql from 'graphql-tag';
2import { Query } from 'react-apollo';
3
4const GET_DOGS = gql`
5 {
6 dogs {
7 id
8 breed
9 }
10 }
11`;
Next, we'll create a component named Dogs
. Inside it, we'll pass our GET_DOGS
query to the useQuery
hook:
1function Dogs({ onDogSelected }) {
2 const { loading, error, data } = useQuery(GET_DOGS);
3
4 if (loading) return 'Loading...';
5 if (error) return `Error! ${error.message}`;
6
7 return (
8 <select name="dog" onChange={onDogSelected}>
9 {data.dogs.map(dog => (
10 <option key={dog.id} value={dog.breed}>
11 {dog.breed}
12 </option>
13 ))}
14 </select>
15 );
16}
1const Dogs = ({ onDogSelected }) => (
2 <Query query={GET_DOGS}>
3 {({ loading, error, data }) => {
4 if (loading) return 'Loading...';
5 if (error) return `Error! ${error.message}`;
6
7 return (
8 <select name="dog" onChange={onDogSelected}>
9 {data.dogs.map(dog => (
10 <option key={dog.id} value={dog.breed}>
11 {dog.breed}
12 </option>
13 ))}
14 </select>
15 );
16 }}
17 </Query>
18);
As our query executes and the values of loading
, error
, and data
change, the Dogs
component can intelligently render different UI elements according to the query's state:
As long as
loading
istrue
(indicating the query is still in flight), the component presents aLoading...
notice.When loading is
false
and there is noerror
, the query 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 query that uses GraphQL variables.
Caching query results
Whenever Apollo Client fetches query results from your server, it automatically caches those results locally. This makes subsequent executions of the same query 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:
1const GET_DOG_PHOTO = gql`
2 query Dog($breed: String!) {
3 dog(breed: $breed) {
4 id
5 displayImage
6 }
7 }
8`;
9
10function DogPhoto({ breed }) {
11 const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
12 variables: { breed },
13 });
14
15 if (loading) return null;
16 if (error) return `Error! ${error}`;
17
18 return (
19 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
20 );
21}
1const GET_DOG_PHOTO = gql`
2 query Dog($breed: String!) {
3 dog(breed: $breed) {
4 id
5 displayImage
6 }
7 }
8`;
9
10const DogPhoto = ({ breed }) => (
11 <Query query={GET_DOG_PHOTO} variables={{ breed }}>
12 {({ loading, error, data }) => {
13 if (loading) return null;
14 if (error) return `Error! ${error}`;
15
16 return (
17 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
18 );
19 }}
20 </Query>
21);
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 variables we want to pass to our GraphQL query. 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 query results is handy and easy to do, but sometimes you want to make sure that cached data is up to date with your server. Apollo Client supports two strategies for this: polling and refetching.
Polling
Polling provides near-real-time synchronization with your server by causing a query 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:
1function DogPhoto({ breed }) {
2 const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
3 variables: { breed },
4 skip: !breed,
5 pollInterval: 500,
6 });
7
8 if (loading) return null;
9 if (error) return `Error! ${error}`;
10
11 return (
12 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
13 );
14}
1const DogPhoto = ({ breed }) => (
2 <Query
3 query={GET_DOG_PHOTO}
4 variables={{ breed }}
5 skip={!breed}
6 pollInterval={500}
7 >
8 {({ loading, error, data }) => {
9 if (loading) return null;
10 if (error) return `Error! ${error}`;
11
12 return (
13 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
14 );
15 }}
16 </Query>
17);
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 query will not poll.
You can also start and stop polling dynamically with the
startPolling
andstopPolling
functions that are returned by theuseQuery
hook.
Refetching
Refetching enables you to refresh query 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 query'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 query uses the same variables that it used in its previous execution.
1function DogPhoto({ breed }) {
2 const { loading, error, data, refetch } = useQuery(GET_DOG_PHOTO, {
3 variables: { breed },
4 skip: !breed,
5 });
6
7 if (loading) return null;
8 if (error) return `Error! ${error}`;
9
10 return (
11 <div>
12 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
13 <button onClick={() => refetch()}>Refetch!</button>
14 </div>
15 );
16}
1const DogPhoto = ({ breed }) => (
2 <Query query={GET_DOG_PHOTO} variables={{ breed }} skip={!breed}>
3 {({ loading, error, data, refetch }) => {
4 if (loading) return null;
5 if (error) return `Error! ${error}`;
6
7 return (
8 <div>
9 <img
10 src={data.dog.displayImage}
11 style={{ height: 100, width: 100 }}
12 />
13 <button onClick={() => refetch()}>Refetch!</button>
14 </div>
15 );
16 }}
17 </Query>
18);
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 query'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 query via the networkStatus
property. To take advantage
of this information, we need to set the notifyOnNetworkStatusChange
option to true
so our query component re-renders while a refetch is in flight:
1function DogPhoto({ breed }) {
2 const { loading, error, data, refetch, networkStatus } = useQuery(
3 GET_DOG_PHOTO,
4 {
5 variables: { breed },
6 skip: !breed,
7 notifyOnNetworkStatusChange: true,
8 },
9 );
10
11 if (networkStatus === 4) return 'Refetching!';
12 if (loading) return null;
13 if (error) return `Error! ${error}`;
14
15 return (
16 <div>
17 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
18 <button onClick={() => refetch()}>Refetch!</button>
19 </div>
20 );
21}
1const DogPhoto = ({ breed }) => (
2 <Query
3 query={GET_DOG_PHOTO}
4 variables={{ breed }}
5 skip={!breed}
6 notifyOnNetworkStatusChange
7 >
8 {({ loading, error, data, refetch, networkStatus }) => {
9 if (networkStatus === 4) return 'Refetching!';
10 if (loading) return null;
11 if (error) return `Error! ${error}`;
12
13 return (
14 <div>
15 <img
16 src={data.dog.displayImage}
17 style={{ height: 100, width: 100 }}
18 />
19 <button onClick={() => refetch()}>Refetch!</button>
20 </div>
21 );
22 }}
23 </Query>
24);
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 source.
Inspecting error states
You can customize your query error handling by providing the errorPolicy
configuration option to the useQuery
hook. The default value is none
, which tells Apollo Client to treat all GraphQL errors as runtime errors. In this case, Apollo Client discards any query 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 query response data, allowing you to render partial results.
Executing queries manually
When React mounts and renders a component that calls the useQuery
hook, Apollo Client automatically executes the specified query. 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 query. Instead, it returns a function in its result tuple that you can call whenever you're ready to execute the query:
1import React, { useState } from 'react';
2import { useLazyQuery } from '@apollo/react-hooks';
3
4function DelayedQuery() {
5 const [dog, setDog] = useState(null);
6 const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
7
8 if (loading) return <p>Loading ...</p>;
9
10 if (data && data.dog) {
11 setDog(data.dog);
12 }
13
14 return (
15 <div>
16 {dog && <img src={dog.displayImage} />}
17 <button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
18 Click me!
19 </button>
20 </div>
21 );
22}
To view a complete version of the app we just built, check out the CodeSandbox here.
useQuery API
Supported options and result fields 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 API reference.
Options
The useQuery
hook accepts the following options:
Option | Type | Description |
---|---|---|
query | DocumentNode | A 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 |
pollInterval | number | Specifies the interval in ms at which you want your component to poll for data. Defaults to 0 (no polling). |
notifyOnNetworkStatusChange | boolean | Whether updates to the network status or network error should re-render your component. Defaults to false. |
fetchPolicy | FetchPolicy | How you want your component to interact with the Apollo cache. Defaults to "cache-first". |
errorPolicy | ErrorPolicy | How you want your component to handle network and GraphQL errors. Defaults to "none", which means we treat GraphQL errors as runtime errors. |
ssr | boolean | Pass in false to skip your query during server-side rendering. |
displayName | string | The name of your component to be displayed in React DevTools. Defaults to 'Query'. |
skip | boolean | If skip is true, the query will be skipped entirely. Not available with useLazyQuery . |
onCompleted | (data: TData {}) => void | A callback executed once your query successfully completes. |
onError | (error: ApolloError) => void | A callback executed in the event of an error. |
context | Record<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. |
partialRefetch | boolean | If 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. |
client | ApolloClient | An ApolloClient instance. By default useQuery / Query uses the client passed down via context, but a different client can be passed in. |
returnPartialData | boolean | Opt 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 query result, plus some helpful functions for refetching, dynamic polling, and pagination.
Property | Type | Description |
---|---|---|
data | TData | An object containing the result of your GraphQL query. Defaults to undefined . |
loading | boolean | A boolean that indicates whether the request is in flight |
error | ApolloError | A runtime error with graphQLErrors and networkError properties |
variables | { [key: string]: any } | An object containing the variables the query was called with |
networkStatus | NetworkStatus | A 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) => void | This function sets up an interval in ms and fetches the query each time the specified interval passes. |
stopPolling | () => void | This function stops the query from polling. |
subscribeToMore | (options: { document: DocumentNode, variables?: TVariables, updateQuery?: Function, onError?: Function}) => () => void | A function that sets up a subscription. subscribeToMore returns a function that you can use to unsubscribe. |
updateQuery | (previousResult: TData, options: { variables: TVariables }) => TData | A function that allows you to update the query's result in the cache outside the context of a fetch, mutation, or subscription |
client | ApolloClient | Your ApolloClient instance. Useful for manually firing queries or writing data to the cache. |
called | boolean | A 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, learn how to update your data with the useMutation
hook!
After that, learn about some other handy Apollo Client features:
Local state management: Learn how to query local data.
Pagination: Building lists has never been easier thanks to Apollo Client's
fetchMore
function. Learn more in our pagination tutorial.