Typed Loading and Error States
When you build React components that rely on asynchronous data—such as fetching information from an API—you need to handle several possible UI states. The three most common states are: loading (the data is being fetched), error (the request failed), and success (the data was received). Using TypeScript, you can explicitly type these states to make your UI logic safer and your code easier to understand.
To manage these scenarios, you often use a combination of state variables: one for the loading status, one for any error, and one for the actual data. However, this approach can lead to bugs if, for example, the UI tries to display data when it's still loading or when an error has occurred. TypeScript helps you prevent these issues by allowing you to represent the possible states as a single typed object. This is where discriminated unions become especially useful.
A discriminated union is a TypeScript pattern that lets you define a type as one of several possible shapes, each with a distinguishing property (often called a "tag" or "kind"). For async UI, you can define a union type where each state—loading, error, or success—has a unique tag and its own associated data. This approach ensures that your UI logic can only access data when it is actually available, and that you must handle all possible states in your rendering code.
Here is a practical example of using a discriminated union to manage async state in a React component:
// Define the async state type using a discriminated union
// The generic <T> allows this pattern to be reused for any data type
type AsyncState<T> =
| { status: "loading" }
| { status: "error"; error: string }
| { status: "success"; data: T };
function UserProfile() {
// State is typed to only allow one of the defined shapes
const [state, setState] = React.useState<AsyncState<User>>({ status: "loading" });
React.useEffect(() => {
fetchUser()
.then(user => setState({ status: "success", data: user }))
.catch(err => setState({ status: "error", error: err.message }));
}, []);
if (state.status === "loading") {
return <div>Loading...</div>;
}
if (state.status === "error") {
return <div>Error: {state.error}</div>;
}
// state.status === "success"
return <div>Hello, {state.data.name}!</div>;
}
In this example, the AsyncState type uses a discriminated union to represent three mutually exclusive states. The status property acts as the discriminator, ensuring that only the relevant properties are available in each state. This approach prevents you from accidentally accessing data or error when they are not present, and makes your component logic more predictable and type-safe.
1. What is a discriminated union useful for in managing async state?
2. Why is it important to type loading and error states in React?
¡Gracias por tus comentarios!
Pregunte a AI
Pregunte a AI
Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla
Can you explain how discriminated unions improve type safety in this context?
What are some common mistakes developers make when handling async state without discriminated unions?
Can you show how to extend this pattern for more complex async flows, like pagination or refetching?
Awesome!
Completion rate improved to 4.17
Typed Loading and Error States
Desliza para mostrar el menú
When you build React components that rely on asynchronous data—such as fetching information from an API—you need to handle several possible UI states. The three most common states are: loading (the data is being fetched), error (the request failed), and success (the data was received). Using TypeScript, you can explicitly type these states to make your UI logic safer and your code easier to understand.
To manage these scenarios, you often use a combination of state variables: one for the loading status, one for any error, and one for the actual data. However, this approach can lead to bugs if, for example, the UI tries to display data when it's still loading or when an error has occurred. TypeScript helps you prevent these issues by allowing you to represent the possible states as a single typed object. This is where discriminated unions become especially useful.
A discriminated union is a TypeScript pattern that lets you define a type as one of several possible shapes, each with a distinguishing property (often called a "tag" or "kind"). For async UI, you can define a union type where each state—loading, error, or success—has a unique tag and its own associated data. This approach ensures that your UI logic can only access data when it is actually available, and that you must handle all possible states in your rendering code.
Here is a practical example of using a discriminated union to manage async state in a React component:
// Define the async state type using a discriminated union
// The generic <T> allows this pattern to be reused for any data type
type AsyncState<T> =
| { status: "loading" }
| { status: "error"; error: string }
| { status: "success"; data: T };
function UserProfile() {
// State is typed to only allow one of the defined shapes
const [state, setState] = React.useState<AsyncState<User>>({ status: "loading" });
React.useEffect(() => {
fetchUser()
.then(user => setState({ status: "success", data: user }))
.catch(err => setState({ status: "error", error: err.message }));
}, []);
if (state.status === "loading") {
return <div>Loading...</div>;
}
if (state.status === "error") {
return <div>Error: {state.error}</div>;
}
// state.status === "success"
return <div>Hello, {state.data.name}!</div>;
}
In this example, the AsyncState type uses a discriminated union to represent three mutually exclusive states. The status property acts as the discriminator, ensuring that only the relevant properties are available in each state. This approach prevents you from accidentally accessing data or error when they are not present, and makes your component logic more predictable and type-safe.
1. What is a discriminated union useful for in managing async state?
2. Why is it important to type loading and error states in React?
¡Gracias por tus comentarios!