Conditionally calling an API using React-Query hook

Viewed 20259

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?

2 Answers

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.

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}-&nbsp;</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.

Related