Search code examples
typescriptreact-nativegraphqlapollo

Apollo client useQuery hook stuck at loading state for queries


I am trying to make api calls using the useQuery hook of @apollo/client but the loading props of the query is always true so the Loader is always shown. I first tried this by calling multiple useQuery hooks one after another, but later found out that even a single query execution is giving this state. I am sharing the relevant details below. I read this answer but it spoke about versioning, which is not solving my problem. Here are my code, dependencies and details about where I am calling this hook.

Home.tsx

// necessary imports
import * as query from 'queries';
import {useQuery} from '@apollo/client';

let obj = {
  "Skin":"",
  "Hair":"",
  "Beard":"",
  "Wellness":"",
  "Performance":""
}

const encode = (str:string):string => Buffer.from(str,'binary').toString('base64');

const Home = () => {
  const queryString = query.GET_COLLECTIONS_BY_HANDLE;

  const hair = useQuery(queryString.query,{
      variables:{
        handle:'hair'
      }
  });

  const skin = useQuery(queryString.query,{
    variables:{
      handle:'skin'
    }
  });

  const beard = useQuery(queryString.query,{
    variables:{
      handle:'beard'
    }
  });

  const wellness = useQuery(queryString.query,{
    variables:{
      handle:'wellness'
    }
  });

  const performance = useQuery(queryString.query,{
    variables:{
      handle:'performance'
    }
  });

  if(hair.loading || skin.loading || wellness.loading || performance.loading || beard.loading){
    console.log('Loading hair',hair.loading);
    console.log('Loading skin',skin.loading);
    console.log('Loading wellness',wellness.loading);
    console.log('Loading beard',beard.loading);
    console.log('Loading performance',performance.loading);
    return <Loader/> // This is my custom Activity Indicator
  }

  if(hair.error || wellness.error || skin.error || performance.error || beard.error){
    console.error(hair.error);
  }
  
  if(hair.data != undefined){
    obj['Hair'] = hair.data.id;
  }

  if(skin.data != undefined){
    obj['Skin'] = skin.data.id;
  }


  if(wellness.data != undefined){
    obj['Wellness'] = wellness.data.id;
  }

  if(performance.data != undefined){
    obj['Performance'] = performance.data.id;
  }

  if(beard.data != undefined){
    obj['Beard'] = beard.data.id;
  }

    return (
      //rendering the UI
  )
}

export default Home

package.json


{
  "dependencies": {
    "@apollo/client": "^3.8.7",
     //other packages
    "react": "18.2.0",
    "react-native": "0.72.6",
    "react-native-collapsible": "^1.6.1",
    "react-native-element-dropdown": "^2.10.1",
    "react-native-gesture-handler": "^2.14.0",
    "react-native-image-slider-show": "^1.0.3",
    "react-native-keychain": "^8.1.2",
    "react-native-reanimated": "^3.6.1",
    "react-native-render-html": "^6.3.4",
    "react-native-safe-area-context": "^4.7.4",
    "react-native-screens": "^3.27.0",
    "react-native-snackbar": "^2.6.2",
    "react-native-vector-icons": "^10.0.2",
    "ts-mixer": "^6.0.3"
  },
  "devDependencies": {
    //development Dependencies
  },
  "engines": {
    "node": ">=16"
  }
}

Navigation structure : Drawer on Top -> Tab Navigation -> Stack Navigation.

When the App loads, the first screen shown in Home.tsx and the loader is continuously shown.


Solution

  • Using useQuery more than once in a component is an anti-pattern. You can run all those queries at once and only have one loading state. Simpler, faster, cleaner.

    Example:

    query twoQueries($p1: string!, $p2: string!) {
      query1(p1: $p1) {
        field1
        field2
        …
      }
      query2(p2: $p2) {
        fieldA
        fieldB
        …
      }
    }
    

    In depth blog post on this topic