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:
- 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??
- 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?
- I just start, any suggestions for this kind of problems? any best pratices?
thank you so much!
CodePudding user response:
- 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
usingObject.assign
or separateloading
anderror
.
const [loading, setLoading] = React.useState(false)
const [error, setError] = React.useState(false)
- 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
}
}
`