Home > Enterprise >  How to show loading spinner while loading data using multiple 'useQuery'
How to show loading spinner while loading data using multiple 'useQuery'

Time:01-03

I am studying GraphQL/Apollow and React, now I want to fetch data using useQuery(or useLazyQuery). More specifically, I have two queries, query B is dependent on the result of query A, that is, I need to skip query B for some query A results, and not skip for other results. Moreover, I want to have loading spinner while fetching data.

My current solution: useEffect with [] parameters useLazyQuery (Apollo) to only load data for one time, and at the same time updating loading state.

export const QUERY_A = gql `
  // something
`
export const QUERY_B = gql `
 // something
`

export default function ExampleModal(props: SomeType) {
  const [apiStatus, setApiStatus] = React.useState({
     loading: false,
     error: false
  });

  let resultA = '';
  const[getA] = useLazyQuery(QUERY_A, {
     onCompleted: (data) => { // won't  set loading to false, as we need to queryB
        resultA = data?.getAquery.status
     },
     one rror: (error) => {
       setApiStatus({  
         loading: false,
         error: true
      });
     },
   });
   
   let resultB = '';
   
  const[getB] = useLazyQuery(QUERY_B, {
     onCompleted: (data) => {
        resultB = data?.getBquery.result;
        setApiStatus({   
         loading: false,
         error: false,
      });
     },
     one rror: (error) => {
       setApiStatus({  
         loading: false,
         error: true
      });
     },
   },
   skip: resultA == 'something'   // this situation,we skip B
   );

  React.useEffect( () => {  
    setApiStatus({   // start loading
         loading: true,
         error: false
    });
 
    // fetch real data from A and B, 
    
    
  }, []); // use [] to just load for one time, when re-render, will not call apis again

  if (apiStatus.loading) return <div> loading...<div>
  if (apiStatatus.error) return <div> error...<div>
  return <div> real result <div>
} 

I know there may be a few issues here, my questions is:

  1. does the useEffect will stop immediately after state changes, saying when I
     setApiStatus({   // start loading
         loading: true,
         error: false
     });

will the component re-render immediately??

  1. How to store resultA and resultB, to not let them don't fetched again after re-render(I mean when loading stopped and I can get real result); should I do something like
    const [apiStatus, setApiStatus] = React.useState({
        loading: false,
        error: false,
        resultA: '',
        resultB: '',
     }); 

will this work?

  1. I just start, any suggestions for this kind of problems? any best pratices?

thank you so much!

CodePudding user response:

  1. Your component will never rerender because you have used an object in the state. React doesn't do deep checks on the state. Either create a new object when doing setState using Object.assign or separate loading and error.
const [loading, setLoading] = React.useState(false)
const [error, setError] = React.useState(false)

  1. I believe the invocation for GET B should happen inside the callback of GET A so you should only worry about how/when to call GET A.

You can use an empty useEffect and make API call to A conditionally if DATA A is undefined/null.

const [dataA, setDataA] = React.useState(null)
const [dataB, setDataB] = React.useState(null)

useEffect(() => {
 !dataA && apiCallForA
}, [])

CodePudding user response:

you can | loadings like

const [callFirst, {loading:loadingFirst}] = useLazyQuery(
    GET_QUERY,
    { variables: ... }
  );
const [callFirst, {loading:loadingSecond}] = useLazyQuery(
    GET_QUERY,
    { variables: ... }
  );

return ((loadingFirst | loadingSecond) && loading...)

return <Component/>

or you can call multiple query in one query

export const MULTIPLE_QUERY=gql`
query ($id:Int){
  post(id:$id){
    id 
    title
}
  comments(id:$id){
    id 
    title
}
}

`

  • Related