Intro
In 2019, memorable moments included Snapchat's introduction of the baby face filter, the conclusion of Game of Thrones (before any further disappointments), and the emergence of React Query, also known as TanStack Query.
React Query, a potent data-fetching library for React, revolutionized our approach to API interactions and server state management.
When it comes to querying data, you often want to trigger the useQuery
hook as a result of a specific user action, such as clicking a button. However, using hooks conditionally or within callbacks is not possible. The implementation varies based on factors like the need for query parameters, one-time versus recurring fetches, and action-specific parameters.
In this article, we'll explore all three scenarios and delve into diverse implementations using React Query.
Fetching Without Params
When you want to initiate a fetch without any parameters as a consequence of an action, this represents the simplest of the three use cases.
In this scenario, the useQuery
hook provides us with a refetch
function that serves our purpose seamlessly.
const WithRefetch = () => {
const { data, dataUpdatedAt, refetch } = useQuery('my-key', () => request(), {
refetchOnWindowFocus: false,
// this prevents the query from firing automatically
enabled: false,
})
console.log("with refetch ", { data, dataUpdatedAt });
return (
<div>
<button onClick={() => refetch()}>Click me!</button>
<br />
{JSON.stringify(data)}
</div>
);
};
In this example, we are setting the enabled
to false
to prevent the query from being automatically called when the render of the component happens.
Then we can just simply call the refetch
function on click (or any other event or side-effect) to fetch the data.
The main limitation with this approach is that the refetch
function does not allow any params to be passed, so if you need to update the params for the query, this would not be the right approach for you.
In this example, we set the enabled
flag to false
to prevent the query from automatically triggering during component rendering.
Subsequently, we can invoke the refetch
function upon a click event (or any other relevant event or side-effect) to fetch the data.
However, it's important to note that this approach has a limitation: the refetch
function does not allow parameters. If you need to update query parameters, this method may not be the best method.
Fetch Once with Params
To introduce parameters into our request, a slight adjustment in our logic needs to happen. Additionally, creating a custom hook to separate the logic can make the process more straightforward:
const useUser = () => {
const [variables, setVariables] = React.useState();
const key = ["user", variables];
const query = useQuery(key, () => request(variables), {
refetchOnWindowFocus: false,
enabled: Boolean(variables)
});
return {
...query,
fetchUser: setVariables,
};
};
Now, let's break down how this custom hook works. The key difference here lies in dynamically configuring the enabled
property based on our parameters. The request won't be triggered when there are no parameters (initial state).
Furthermore, we return the fetchUser
function, essentially working as the state setter to update the enabled
property in response to new parameters, which are consistently supplied to the request: request(variables)
.
Next, let's explore how to use this hook:
const SingleFetchWithParams = () => {
const { data, dataUpdatedAt, fetchUser } = useUser();
// This will log the timestamp for when the query most recent successful query
console.log("single fetch with params", { data, dataUpdatedAt });
return (
<>
<button onClick={() => fetchUser({ id: 2 })}>Click me!</button>
<br />
{data && JSON.stringify(data)}
</>
);
};
In the UI we can call the fetchUser
function returned from the hook with the desired parameters. This will always trigger a new fetch request as long as the params change.
Fetch on Action with Params
If your use case needs triggering a fetch to consistently get the most up-to-date data from the server, regardless of whether you require parameters or if they have changed, then we must use a different technique, leveraging the queryClient
:
const WithRefetch = () => {
const client = useQueryClient();
const id = 1;
// We still need to call the query hook to access the data
const { data, dataUpdatedAt } = useQuery('my-key', () => request(), {
refetchOnWindowFocus: false,
enabled: false,
});
const onClick = () => client.fetchQuery('my-key', () => request({ id }));
console.log("single fetch with params", { data, dataUpdatedAt });
return (
<div>
<button onClick={onClick}>Click me!</button>
<br />
{JSON.stringify(data)}
</div>
);
};
We can access the QueryClient
instance by calling the useQueryClient
hook. Then we can call the fetchQuery
method to trigger the fetch request when required, with the necessary params. This method ensures that a request is consistently triggered every time, guaranteeing access to the most current data.
We still need to use the useQuery
hook, simply because it allows us to access the data returned by the fetch.
This approach was recommended by Tanner Linsley the creator of the library, so we know for a fact we are not breaking any good practices.
One Custom Hook to Rule Them All
We can go to the next level by creating a helper hook that handles all the different use cases that we just covered and simplifies the code implementation:
const useUser = () => {
const client = useQueryClient();
const [variables, setVariables] = React.useState();
const key = ["user", variables];
const query = useQuery(key, () => request(variables), {
refetchOnWindowFocus: false,
enabled: Boolean(variables)
});
return {
...query,
// single fetch approach
fetchUser: setVariables,
// fetch always
fetchTodoWithParams: ({ id }) => client.fetchQuery(key, () => request({ id }))
};
};
Conclusion
We tackled three key use cases:
Fetching Without Parameters: We learned to initiate a fetch on user actions using the
refetch
function from theuseQuery
hook, even though it lacks parameter support.Fetching Once with Params: To fetch data with parameters, we created a custom hook that dynamically enabled/disabled queries based on parameters, allowing flexibility in fetching data with different criteria.
Fetching on Action with Params: For consistent server updates regardless of parameter changes, we utilized the
queryClient
andfetchQuery
method.
You can find all the code and a working demo in codesanbox.
Hope you enjoyed learning about how to use useQuery to fetch data on side-effects and actions ๐, until the next post ๐๐ป.