Reputation: 4332
I am using react-query to make API calls, and in this problem case I want to only call the API if certain conditions are met.
I have an input box where users enter a search query. When the input value is changed, a search server is called with the contents of the input as the search query ... but only if the input value is more than 3 chars long.
In my react component I'm calling:
const {data, isLoading} = useQuery(['search', searchString], getSearchResults);
And my getSearchResults
function will, conditionally, make an API call.
const getSearchResults = async (_, searchString) => {
if (searchString.length < 3)
return {data: []}
const {data} = await axios.get(`/search?q=${searchString}`)
return data;
}
We can't use a hook inside a conditional - so I put the condition into my API calling function.
This almost works. If I enter a short query string, there is no API request made and I get an empty array back for the data
. Yay!
But - isLoading
will flip to true
briefly - even though there is no HTTP request being made. So my loading indicator shows when there is no actual network activity.
Am I misunderstanding how to best solve my use case, is there a way to enure that isLoading
will return false if there is no HTTP activity?
Upvotes: 65
Views: 59497
Reputation: 4332
The key was to use Dependent Queries
So, in my main component, I create a boolean and pass that to the enabled
option of the useQuery
hook:
const isLongEnough = searchString.length > 3;
const {data, isLoading} = useQuery(['search', searchString], getSearchResults, {enabled: isLongEnough});
and the API calling method is simply the API call - not any conditional:
const getSearchResults = async (_, searchString) => {
const {data} = await axios.get(`/search?q=${searchString}`);
return data;
}
The docs describe dependent queries as a solution for loading data from subsequent API endpoints, but the enable
option can accept any boolean. In this case - if the search query string is long enough.
Upvotes: 135
Reputation: 1013
There's another option which is to use queryClient.fetchQuery
API, which gives you the ability to conditionally call the query to fetch the data.
function Example2() {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
if (isLoading) return "Loading...";
if (error) return "An error has occurred: " + error;
return (
<div>
<button
onClick={async () => {
try {
setIsLoading(true);
const posts = await queryClient.fetchQuery(
["postsUsingFetchQuery"],
{
queryFn: () =>
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((res) => res.data)
}
);
setData(posts);
} catch (e) {
setError(e);
}
setIsLoading(false);
}}
>
Fetch posts using fetchQuery{" "}
</button>
<h1>Posts</h1>
{data?.map((post) => {
return (
<div style={{ display: "flex" }}>
<span>{post.id}- </span>
<div>{post.title}</div>
</div>
);
})}
</div>
);
}
On the button click handler, we’ve added the implementation to fetch the posts using queryClient.fetchQuery.
You can read more from this link.
Upvotes: 2