Also known as #remix.
throw
from actions will naturally be caught by the nearestErrorBoundary
.Route.ComponentProps['actionData']
is only useful client-side. Don't spend x-minutes wondering why your console.log insists on being undefined.- Instead of throwing from an action, you can
try/catch
and returndata()
with astatus: 400
.
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData()
const emailAddress = formData.get("email_address")
if (typeof emailAddress !== "string") {
throw new Error("Expected a string value, but received a File.")
}
try {
const validEmailAddress = z.string().email().parse(emailAddress)
await someSideEffect(validEmailAddress) // like authentication
return data({ ok: true as const, emailAddress }, { status: 200 })
} catch (error) {
if (error instanceof ZodError) {
return data(
{ ok: false as const, emailAddress, error: "validation" as const },
{ status: 400 },
)
}
return data(
{ ok: false as const, emailAddress, error: "unknown" as const },
{ status: 400 },
)
}
}
export default function Route({
loaderData,
actionData,
}: Route.ComponentProps) {
// @NOTE: actionData only exists client-side
return (
<>
{actionData?.ok ? (
<p>hooray!</p>
) : (
<Form method="post">
<input
name="email_address"
placeholder="Email address"
required type="email"
/>
<button>submit</button>
</Form>
)}
</>
)
}
Notes #
- I think it is bizarre that
react-router
documentation useslet
everywhere. - It's also weird to
throw
theirredirect
from actions. That ain't an error.