---
id: refetching-queries-with-different-data
title: Refetching Queries with Different Data
slug: /guided-tour/refetching/refetching-queries-with-different-data/
description: Relay guide to refetching queries with different data
keywords:
- refetching
- query
---

import DocsRating from '@site/src/core/DocsRating';
import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
// @fb-only
// @fb-only
// @fb-only

When referring to **"refetching a query"**, we mean fetching the query again for *different* data than was originally rendered by the query. For example, this might be to change a currently selected item, to render a different list of items than the one being shown, or more generally to transition the currently rendered content to show new or different content.

## When using `useQueryLoader` / `loadQuery`

Similarly to [Refreshing Queries with `useQueryLoader`](../refreshing-queries/#when-using-usequeryloader--loadquery), we can also use the `useQueryLoader` Hook described in our [Fetching Queries for Render](../../rendering/queries/#fetching-queries-for-render) section, but this time passing *different query variables*:

<FbInternalOnly>
  // @fb-only
</FbInternalOnly>

<OssOnly>

```js
/**
 * App.react.js
 */
const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const variables = {id: '4'};
  const [queryRef, loadQuery] = useQueryLoader(
    AppQuery,
    props.appQueryRef /* initial query ref */
  );

  const refetch = useCallback(() => {
    // Load the query again using the same original variables.
    // Calling loadQuery will update the value of queryRef.
    loadQuery({id: 'different-id'});
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        refetch={refetch}
        queryRef={queryRef}
      />
    </React.Suspense>
  );
}
```

```js
/**
 * MainContent.react.js
 */

// Renders the preloaded query, given the query reference
function MainContent(props) {
  const {refetch, queryRef} = props;
  const data = usePreloadedQuery(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          friends {
            count
          }
        }
      }
    `,
    queryRef,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <div>Friends count: {data.user?.friends?.count}</div>
      <Button
        onClick={() => refetch()}>
        Fetch latest count
      </Button>
    </>
  );
}
```

Let's distill what's going on here:

* We call `loadQuery` in the event handler for refetching, so the network request starts immediately, and then pass the `queryRef` to `usePreloadedQuery`, so it renders the updated data.
* We are not passing a `fetchPolicy` to `loadQuery`, meaning that it will use the default value of `'store-or-network'`. We could provide a different policy in order to specify whether to use locally cached data (as we covered in [Reusing Cached Data For Render](../../reusing-cached-data/)).
* Calling `loadQuery` will re-render the component and may cause `usePreloadedQuery` to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)). This means that we'll need to make sure that there's a `Suspense` boundary wrapping the `MainContent` component, in order to show a fallback loading state.

</OssOnly>


### If you need to avoid Suspense

In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state:

<FbInternalOnly>
  // @fb-only
</FbInternalOnly>

```js
/**
 * App.react.js
 */
const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const environment = useRelayEnvironment();
  const [queryRef, loadQuery] = useQueryLoader(
    AppQuery,
    props.appQueryRef /* initial query ref */
  );
  const [isRefetching, setIsRefetching] = useState(false)

  const refetch = useCallback(() => {
    if (isRefetching) { return; }
    setIsRefetching(true);
    const variables = { id: 'different-id' };

    // fetchQuery will fetch the query and write
    // the data to the Relay store. This will ensure
    // that when we re-render, the data is already
    // cached and we don't suspend
    fetchQuery(environment, AppQuery, variables)
      .subscribe({
        complete: () => {
          setIsRefetching(false);

          // *After* the query has been fetched, we call
          // loadQuery again to re-render with a new
          // queryRef.
          // At this point the data for the query should
          // be cached, so we use the 'store-only'
          // fetchPolicy to avoid suspending.
          loadQuery(variables, {fetchPolicy: 'store-only'});
        },
        error: () => {
          setIsRefetching(false);
        }
      });
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        isRefetching={isRefetching}
        refetch={refetch}
        queryRef={queryRef}
      />
    </React.Suspense>
  );
}
```

Let's distill what's going on here:

* When refetching, we now keep track of our own `isRefetching` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI inside the `MainContent` component, *without* hiding the `MainContent`.
* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we call `loadQuery` so that we obtain an updated `queryRef` that we then pass to `usePreloadedQuery` in order render the updated data, similar to the previous example.
* At this point, when `loadQuery` is called, the data for the query should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data.

## When using `useLazyLoadQuery`

Similarly to [Refreshing Queries with `useLazyLoadQuery`](../refreshing-queries/#when-using-uselazyloadquery), we can also use the [`useLazyLoadQuery`](../../../api-reference/use-lazy-load-query/) Hook described in our [Lazily Fetching Queries during Render](../../rendering/queries/#lazily-fetching-queries-during-render) section, but this time passing *different query variables*:

<FbInternalOnly>
  // @fb-only
</FbInternalOnly>

<OssOnly>

```js
/**
 * App.react.js
 */
const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const [queryArgs, setQueryArgs] = useState({
    options: {fetchKey: 0},
    variables: {id: '4'},
  });

  const refetch = useCallback(() => {
    // Trigger a re-render of useLazyLoadQuery with new variables,
    // *and* an updated fetchKey.
    // The new fetchKey will ensure that the query is fully
    // re-evaluated and refetched.
    setQueryArgs(prev => ({
      options: {
        fetchKey: (prev?.options.fetchKey ?? 0) + 1,
      },
      variables: {id: 'different-id'}
    }));
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        refetch={refetch}
        queryArgs={queryArgs}
      />
    </React.Suspense>
  );
}
```

```js
/**
 * MainContent.react.js
 */
// Fetches and renders the query, given the fetch options
function MainContent(props) {
  const {refetch, queryArgs} = props;
  const data = useLazyLoadQuery(
    graphql`
      query AppQuery($id: ID!) {
        user(id: $id) {
          name
          friends {
            count
          }
        }
      }
    `,
    queryArgs.variables,
    queryArgs.options,
  );

  return (
    <>
      <h1>{data.user?.name}</h1>
      <div>Friends count: {data.user.friends?.count}</div>
      <Button
        onClick={() => refetch()}>
        Fetch latest count
      </Button>
    </>
  );
}
```

Let's distill what's going on here:

* We update the component in the event handler for refreshing by setting new query args in state. This will cause the `MainContent` component that uses `useLazyLoadQuery` to re-render with the new `variables` and  `fetchKey`, and refetch the query upon rendering.
* We are passing a new value of `fetchKey` which we increment on every update. Passing a new `fetchKey` to `useLazyLoadQuery` on every update will ensure that the query is fully re-evaluated and refetched.
* We are not passing a new `fetchPolicy` to `useLazyLoadQuery`, meaning that it will use the default value of `'store-or-network'`. We could provide a different policy in order to specify whether to use locally cached data (as we covered in [Reusing Cached Data For Render](../../reusing-cached-data/)).
* The state update in `refetch` will re-render the component and may cause the component to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)). This means that we'll need to make sure that there's a `Suspense` boundary wrapping the `MainContent` component, in order to show a fallback loading state.


</OssOnly>

### If you need to avoid Suspense

In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state:

<FbInternalOnly>
  // @fb-only
</FbInternalOnly>

```js
/**
 * App.react.js
 */
const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
  const environment = useRelayEnvironment();
  const [isRefreshing, setIsRefreshing] = useState(false)
  const [queryArgs, setQueryArgs] = useState({
    options: {fetchKey: 0, fetchPolicy: 'store-or-network'},
    variables: {id: '4'},
  });

  const refetch = useCallback(() => {
    if (isRefreshing) { return; }
    setIsRefreshing(true);
    const variables = { id: 'different-id' };

    // fetchQuery will fetch the query and write
    // the data to the Relay store. This will ensure
    // that when we re-render, the data is already
    // cached and we don't suspend
    fetchQuery(environment, AppQuery, variables)
      .subscribe({
        complete: () => {
          setIsRefreshing(false);

          // *After* the query has been fetched, we update
          // our state to re-render with the new fetchKey
          // and fetchPolicy.
          // At this point the data for the query should
          // be cached, so we use the 'store-only'
          // fetchPolicy to avoid suspending.
          setQueryArgs(prev => ({
            options: {
              fetchKey: (prev?.options.fetchKey ?? 0) + 1,
              fetchPolicy: 'store-only',
            },
            variables,
          }));
        },
        error: () => {
          setIsRefreshing(false);
        }
      });
  }, [/* ... */]);

  return (
    <React.Suspense fallback="Loading query...">
      <MainContent
        isRefetching={isRefetching}
        refetch={refetch}
        queryArgs={queryArgs}
      />
    </React.Suspense>
  );
}
```

Let's distill what's going on here:

* When refetching, we now keep track of our own `isRefetching` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI inside the `MainContent` component, *without* hiding the `MainContent`.
* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we update our state so that we re-render an updated `fetchKey` and `fetchPolicy` that we then pass to `useLazyLoadQuery` in order render the updated data, similar to the previous example.
* At this point, when we update the state, the data for the query should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data.


<DocsRating />
