The logo of seruco.io
← Go back

Hide useQuery

— By Sebastian Ruhleder

I always wrap React Query's useQuery hook in a custom hook and never use it directly within a component. To see why, let's have a look at an example:

function TodoList() {
const { data: todos = [] } = useQuery(
['todos'],
() => fetchTodos()
);

return (
<ul>
{todos.map(todo => (
<li>{todo}</li>
)}
</ul>
);
}

The TodoList component is quite simple: It fetches a list of to-dos from our backend and renders them within an unordered list. By using useQuery directly, this component has to:

The component is intended to render a to-do list, yet it is responsible for very technical decisions like these. In contrast, let's encapsulate the use of useQuery in a custom hook:

function TodoList() {
const todos = useTodos();

return (
<ul>
{todos.map(todo => (
<li>{todo}</li>
)}
</ul>
);
}

function useTodos() {
const { data: todos = [] } = useQuery(
['todos'],
() => fetchTodos()
);
return todos;
}

By introducing a custom useTodos() hook, we:

The choice of a sensible query key, how a resource is fetched, and the configuration of useQuery's options are implementation details that should always be hidden from components that only want to consume the resource managed by it.

I've used this pattern for quite a while, both in personal projects and at work. It has served me tremendously well. The pattern ties in with and is a concrete instance of Kyle Shevlin's great post useEncapsulation, which I can highly recommend for a more general view on this topic.