Effect Atom provides another API for handling async states: Result.builder.
Replace the existing code inside components/user-grid.tsx with the following:
"use client";
import { Result, useAtomValue } from "@effect-atom/atom-react";
import { FailureCard } from "@/components/failure-card";import { UserEmptyCard } from "@/components/user-empty-card";import { UserGridSpinner } from "@/components/user-grid-spinner";import { UserSuccessCard } from "@/components/user-success-card";
import { usersAtom } from "@/atoms/user";import { ConfigError, GetUsersError } from "@/errors";import { UsersResponse } from "@/schema/user-schema";
const selectUsers = ( result: Result.Result<UsersResponse, ConfigError | GetUsersError>,) => Result.map(result, (data) => data.users);
export function UserGrid() { const usersResult = useAtomValue(usersAtom, selectUsers);
return Result.builder(usersResult) .onInitial(() => <UserGridSpinner />)
.onErrorTag("ConfigError", (error) => ( <FailureCard title="ConfigError" message={error.message} /> ))
.onErrorTag("GetUsersRequestError", (error) => ( <FailureCard title="GetUsersRequestError" message={error.message} /> ))
.onErrorTag("GetUsersResponseError", (error) => ( <FailureCard title="GetUsersResponseError" message={error.message} /> ))
.onErrorTag("GetUsersParseError", (error) => ( <FailureCard title="GetUsersParseError" message={error.message} /> ))
.onDefect(() => ( <FailureCard title="Unexpected Error" message="Something went wrong. Please try refreshing the page." /> ))
.onSuccess((users) => users.length === 0 ? ( <UserEmptyCard reason="empty-users-list" /> ) : ( <div className="grid grid-cols-1 gap-10 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4"> {users.map((user) => ( <UserSuccessCard key={user.id} user={user} /> ))} </div> ), ) .render();}Result.builder provides two advantages over Result.match:
Fine-grained error handling
Instead of handling all errors in a single onFailure callback, you can use onErrorTag to handle specific error types individually. This lets you show different UI for different errors.
Access to the waiting state.
Each handler receives metadata as its second parameter, including a waiting boolean, which indicates whether data is being refreshed in the background. We’ll explore the waiting boolean in detail in an upcoming chapter.
Unlike Result.match, Result.builder doesn’t guarantee exhaustiveness.
TypeScript won’t error if you forget to handle an error type.