Issue
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?
Solution
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)
Answered By - TkDodo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.