Dhaval Jardosh
Dhaval Jardosh

Reputation: 7299

How to fetch multiple conditional queries in Apollo Client React?

I'm using Apollo Client, and for fetching queries I'm using useQuery from the package @apollo/react-hooks.

I would like to accomplish the following:

List of Steps:

Step 1: Fetch a query stage

const GetStage = useQuery(confirmStageQuery, {
  variables: {
    input: {
      id: getId.id
    }
  }
});

Step 2: Based on the response that we get from GetStage, we would like to switch between 2 separate queries

if (!GetStage.loading && GetStage.data.getGame.stage === "Created") {
  Details = useQuery(Query1, {
    variables: {
      input: {
        id: getId.id
      }
    }
  });
} else if (!GetStage.loading && GetStage.data.getGame.stage === "Confirmed") {
  Details = useQuery(Query2, {
    variables: {
      input: {
        id: getId.id
      }
    }
  });
}

Step 3: Also when the page loads every time, I'm re-fetching the data.

useEffect(() => {
  //Fetch for Change in the Stage
  GetStage.refetch();

  //Fetch for Change in the Object
  if (Details) {
    Details.refetch();
    if (Details.data) {
      setDetails(Details.data.getGame);
    }
  }
});

Problem?

Rendered more hooks than during the previous render.

Details.data is undefined

So how can we call multiple async queries in Apollo Client?

Upvotes: 9

Views: 9684

Answers (2)

tarmes
tarmes

Reputation: 15442

As Philip said, you can't conditionally call hooks. However conditionally calling a query is very common, so Apollo allows you to skip it using the skip option:

const { loading, error, data: { forum } = {}, subscribeToMore } = useQuery(GET_FORUM, {
  skip: !forumId,
  fetchPolicy: 'cache-and-network',
  variables: { id: forumId },
});

The hook is called, but the query isn't. That's a lot simpler and clearer than using a lazy query for your use case in my opinion.

Upvotes: 24

Philip Feldmann
Philip Feldmann

Reputation: 8365

The rules of hooks say you can't conditionally call hooks, whenever you find yourself in a situation where you want to use an if/else around a hook you're probably on the wrong track.

What you want to do here is to use a lazyQuery for everything that's "optional" or will be fetched later - or for queries that depend on the result of another query.

Here's a quick example (probably not complete enough to make your entire code work):

// This query is always called, use useQuery
const GetStage = useQuery(confirmStageQuery, {
  variables: {
    input: {
      id: getId.id
    }
  }
});

const [fetchQuery1, { loading1, data1 }] = useLazyQuery(Query1);
const [fetchQuery2, { loading2, data2 }] = useLazyQuery(Query2);

// Use an effect to execute the second query when the data of the first one comes in

useEffect(() => {
  if (!GetStage.loading && GetStage.data.getGame.stage === "Created") {
    fetchQuery1({variables: {
     input: {
        id: getId.id
      }
    }})
  } else if (!GetStage.loading && GetStage.data.getGame.stage === "Confirmed") {
    fetchQuery2({variables: {
     input: {
        id: getId.id
      }
    }})
  } 
}, [GetState.data, GetStage.loading])

Upvotes: 6

Related Questions