I’m using react-query
to make two separate queries in the same React component. I originally tried using two useQuery
hooks:
export default function Component() { const [barData, setBarData] = useState(); const [lineData, setLineData] = useState(); const { error: errorBar, isLoading: loadingBar } = useData( "barQuery", "BAR_TEST_SINGLE", setBarData ); const { error: errorLine, isLoading: loadingLine } = useData( "lineQuery", "LINE_TEST", setLineData ); const isLoading = loadingLine && loadingBar; const error = errorLine && errorBar; if (isLoading) return <LoadingSpinner title={title} />; if (error) return <InvalidStateAPI description={error.message} title={title} />; return ( <> <Line data={lineData} /> <Bar data={barData} /> </> ); }
Here’s the content of useData
, which is imported from another file:
export function useData(queryId, chartId, setData) { return useQuery( queryId, () => fetchData(chartId, "models").then((resp) => { setData(resp.Item.data); }) ); }
fetchData
is a function that queries an AWS DDB table. I’m happy to post that function, but I’m currently excluding it for brevity.
Rendering Component
results in this error: TypeError: Cannot read properties of undefined (reading 'filter')
. I suspect this error is thrown because a component doesn’t get its data in time for rendering. I thought this would be solved by adding const isLoading = loadingLine && loadingBar;
, as suggested in this post, but it did not.
Finally, on the recommendation of this SO answer, I decided to use useQueries
. The documentation is quite sparse, but it recommends the following format:
const results = useQueries([ { queryKey: ['post', 1], queryFn: fetchPost }, { queryKey: ['post', 2], queryFn: fetchPost }, ])
I modified my original code to the following:
const results = useQueries([ { queryKey: ["post", 1], queryFn: useData2("barQuery", "BAR_TEST_SINGLE", setBarData), }, { queryKey: ["post", 2], queryFn: useData2("lineQuery", "LINE_TEST", setLineData), }, ]);
but now I’m getting this error: TypeError: _this2.options.queryFn is not a function
.
Am I formatting my query incorrectly? How can I fix it? Alternatively, are there other ways to run different queries using react-query
in the same component?
Advertisement
Answer
No, this is not the correct syntax for useQueries
. You can’t pass a useQuery hook in as queryFn
– the queryFn
needs the function that fetches the data, in your case, that would be fetchData(chartId, "models")
.
The root cause of your initial problem however seems to be that your condition only waits until one of the queries has finished loading:
const isLoading = loadingLine && loadingBar;
if loadingLine
is false
and loadingBar
is true
, this condition will yield false
and you will thus not display a loading spinner anymore. If you want to wait until all queries have finished loading, that would be:
const isLoading = loadingLine || loadingBar;
lastly, I’d like to point out that copying data from react-query to local state is not necessary. react-query will return the result returned from the queryFn
as the data
field. My implementation would look something like that:
export function useData(queryId, chartId) { return useQuery( [queryId, chartId], () => fetchData(chartId, "models") ); } const { error: errorBar, isLoading: loadingBar, data: barData } = useData( "barQuery", "BAR_TEST_SINGLE", ); const { error: errorLine, isLoading: loadingLine, data: lineData } = useData( "lineQuery", "LINE_TEST", ); const isLoading = loadingLine || loadingBar;
or, alternatively, with useQueries
:
const results = useQueries([ { queryKey: ["barQuery", "BAR_TEST_SINGLE"], queryFn: () => fetchData("BAR_TEST_SINGLE", "models") }, { queryKey: ["lineQuery", "LINE_TEST"], queryFn: () => fetchData("LINE_TEST", "models") }, ]); const isLoading = results.some(result => result.isLoading)