Effect Atom provides another API for handling async states: AsyncResult.builder.
Replace the existing code inside components/user-grid.tsx with the following:
"use client";
import { useAtomValue } from "@effect/atom-react";import { AsyncResult, Atom } from "effect/unstable/reactivity";
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";
const selectUsers = (result: Atom.Type<typeof usersAtom>) => AsyncResult.map(result, (data) => data.users);
export function UserGrid() { const usersResult = useAtomValue(usersAtom, selectUsers);
return AsyncResult.builder(usersResult) .onInitial(() => <UserGridSpinner />) .onErrorTag("ClientError", (error) => ( <FailureCard title="Client Error" message={error.message} /> )) .onErrorTag("ServerError", (error) => ( <FailureCard title="Server Error" message={error.message} /> )) .onErrorTag("ParseError", (error) => ( <FailureCard title="Parse Error" 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();}AsyncResult.builder provides two advantages over AsyncResult.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 the Showing Stale Data During Refresh chapter.
Unlike AsyncResult.match, AsyncResult.builder doesn’t guarantee
exhaustiveness. TypeScript won’t error if you forget to handle an error type.