Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 357
Dhaval Jardosh Avatar asked Nov 19 '19 00:11

Dhaval Jardosh


Video Answer


2 Answers

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.

like image 110
tarmes Avatar answered Oct 30 '22 17:10

tarmes


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])
like image 40
Philip Feldmann Avatar answered Oct 30 '22 18:10

Philip Feldmann