react-apollo-hooks
react-apollo-hooks copied to clipboard
Trying to get my head around fetching data only once, thought useQuery works like useEffect
When using useQuery I've noticed that if I change some state on my component, the query gets rerun as one would expect.
function Index() {
...
const [dialogOpen, setDialogOpen] = useState({ show: false, id: '0' });
...
const { data, error } = useQuery(GET_JOBS, { suspend: true });
if (error) {
return <div>Error! {error.message}</div>;
}
const jobs = data.cxJobs; //This is our data
....
function editCallback(e, more) {
setDialogOpen({ show: true, id: e });
}
....
}
Since I only want it to happen the first time it renders, I assumed it worked like useffect, and that by adding an empty array to the end of the call:
const { data, error } = useQuery(GET_JOBS, { suspend: true }, []);
It turns out that's the not the case. Plus it seems to me there's no easy way to make that happen with useQuery. You can't call a hook conditionally.
Do I need to fallback to using the client directly?
You could utilise skip option in useQuery, the query should be cached so it shouldn't make an actual api call in the first place, but you could have a state dataExist and once data is returned from useQuery you could set dataExist state to true and skip the query.
Thanks, I will experiment with this. I knew there had to be a better way than what I was thinking about doing!
For anyone interested. First I followed @jinshin1013s advice. This is what I wound up doing. It does seem pretty inelegant: super fiddly and imperative:
function Index() {
...
const [jobs, setJobs] = useState([]);
const [dialogOpen, setDialogOpen] = useState({ show: false, id: '0' });
...
const queryOptions =jobs.length >0? { suspend: true, skip:true}: { suspend: true };
const { data, error } = useQuery(GET_JOBS, queryOptions);
if (error) {
return <div>Error! {error.message}</div>;
}
const _jobs = get('cxJobs', data); //This is our data
if(_jobs)
setJobs(_jobs); //Flip the state so we only do it once
....
function editCallback(e, more) {
setDialogOpen({ show: true, id: e });
}
....
}
I rewrote it using useEffect and it's better. I'm just wondering if it be better still.
const [dialogOpen, setDialogOpen] = useState({ show: false, id: '0' });
const [jobs, setJobs] = useState([]);
useEffect(()=> {fetchData()}, []);
const fetchData = async _ => {
const result = await client.query({query:GET_JOBS});
setJobs(get(['data','cxJobs'], result));
}
async function onEventChanged(id, event) {
await mutateOne(client, jobGQL, eventToJob(event));
}
You could do something like this. Also, you might also want to avoid setting the result of the query to useState and just use data directly.
const [skip, setSkip] = React.useState(false)
const { loading, data } = useQuery(QUERY, { skip })
React.useEffect(() => {
// check whether data exists
if (!loading && !!data) {
setSkip(true)
}
}, [data, loading])
if (loading) return <Loading />
// safe to assume data exist
return <Comp jobs={data.cxJobs} />
What if the same query is being used at two different components and I want it to fetch only once and serve it from cache in the other component?
@vish30 — at this point you should be using the useQuery hook from ApolloClient! What version of ApolloClient are you on?