How to Display Loading, Error, and Success States with React Query
Handling different states like loading, error, and success is a common task when working with asynchronous data in React applications. React Query makes this easier and more efficient with its powerful state management features. By using Type Narrowing, we can simplify and streamline how we display these states.
In this post, we’ll explore how to handle these states with React Query using TypeScript. We'll walk through an example to show how you can leverage type narrowing for clear, type-safe logic.
Step 1: Setting Up React Query and TypeScript
First, make sure you have React Query installed in your project. Here’s a basic setup:
npm install @tanstack/react-query
Wrap your app with the QueryClientProvider
and configure the QueryClient
:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
export function App() {
return (
<QueryClientProvider client={queryClient}>
<MyComponent />
</QueryClientProvider>
);
}
Step 2: Using React Query in a Component
We’ll fetch user data from a sample API and display loading, error, and success states:
import { useQuery } from "@tanstack/react-query";
type User = {
id: number;
name: string;
};
export function MyComponent() {
const { data, isLoading, isError } = useQuery<User, Error>(["user"], fetchUser);
if (isLoading) {
return <p>Loading...</p>;
}
if (isError) {
return <p>Error fetching data!</p>;
}
return (
<div>
<h1>User Data</h1>
<p>ID: {data?.id}</p>
<p>Name: {data?.name}</p>
</div>
);
}
async function fetchUser(): Promise<User> {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
if (!response.ok) throw new Error("Network error");
return response.json();
}
Step 3: Leveraging Type Narrowing for Better Clarity
TypeScript automatically narrows types when checking isLoading
or isError
. This means you can write type-safe conditions with ease. Here’s how it works:
isLoading
Narrowing: Ensuresdata
andisError
are irrelevant when loading.isError
Narrowing: Makesdata
accessible only in success states.
Here’s an updated version using TypeScript effectively:
if (isLoading) {
return <p>Loading...</p>; // TypeScript knows data isn't available here
}
if (isError) {
return <p>Error: {error.message}</p>; // TypeScript ensures error exists
}
return (
<div>
<h1>User Details</h1>
<p>ID: {data?.id}</p>
<p>Name: {data?.name}</p>
</div>
);
Conclusion
React Query’s built-in state flags and TypeScript narrowing simplify handling various query states. By understanding how to use isLoading
, isError
, and data
effectively, you can write cleaner and safer code. This approach not only improves readability but also ensures type safety throughout your app.