Apollo Docs
/

Queries

Learn how to fetch data with the useQuery hook


This article was recently updated to reflect Apollo Client's support for React Hooks. To perform queries with Apollo Client's render-prop API instead, see the previous version of this article.

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, simply 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:

index.js
import gql from 'graphql-tag';
import { useQuery } from '@apollo/react-hooks';

const GET_DOGS = gql`
  {
    dogs {
      id
      breed
    }
  }
`;

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

index.js
function Dogs({ onDogSelected }) {
  const { loading, error, data } = useQuery(GET_DOGS);

  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>
  );
}

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 is true (indicating the query is still in flight), the component presents a Loading... notice.
  • When loading is false and there is no error, 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:

index.js
const GET_DOG_PHOTO = gql`
  query Dog($breed: String!) {
    dog(breed: $breed) {
      id
      displayImage
    }
  }
`;

function DogPhoto({ breed }) {
  const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

  return (
    <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
  );
}

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:

index.js
function DogPhoto({ breed }) {
  const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
    skip: !breed,
    pollInterval: 500,
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

  return (
    <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
  );
}

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 and stopPolling functions that are returned by the useQuery 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.

index.js
function DogPhoto({ breed }) {
  const { loading, error, data, refetch } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
    skip: !breed,
  });

  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>
  );
}

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:

index.js
function DogPhoto({ breed }) {
  const { loading, error, data, refetch, networkStatus } = useQuery(
    GET_DOG_PHOTO,
    {
      variables: { breed },
      skip: !breed,
      notifyOnNetworkStatusChange: true,
    },
  );

  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>
  );
}

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:

index.js
import React 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 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:

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 query 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, 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.