Repository: withastro/astro
File: packages/astro/e2e/fixtures/actions-blog/src/components/PostComment.tsx (TypeScript)

Code before:
import { actions, isInputError } from 'astro:actions';
import { useState } from 'react';
import {createLoggerFromFlags} from "../../../../../src/cli/flags.ts";

export function PostComment({
	postId,
	serverBodyError,
}: {
	postId: string;
	serverBodyError?: string;
}) {
	const [comments, setComments] = useState<{ author: string; body: string }[]>([]);
	const [bodyError, setBodyError] = useState<string | undefined>(serverBodyError);
	const [unexpectedError, setUnexpectedError] = useState<string | undefined>(undefined);

	return (
		<>
			<form
				method="POST"
				data-testid="client"
				action={actions.blog.comment.toString()}
				onSubmit={async (e) => {
					e.preventDefault();
					const form = e.target as HTMLFormElement;
					const formData = new FormData(form);
					const { data, error } = await actions.blog.comment(formData);
					if (isInputError(error)) {
						return setBodyError(error.fields.body?.join(' '));
					} else if (error) {
						return setUnexpectedError(`${error.code}: ${error.message}`);
					}
					setBodyError(undefined);
					setComments((c) => [data, ...c]);
					form.reset();
				}}
			>
				{unexpectedError && (
					<p data-error="unexpected" style={{ color: 'red' }}>
						{unexpectedError}
					</p>
				)}
				<input type="hidden" name="postId" value={postId} />
				<label htmlFor="author">Author</label>
				<input id="author" type="text" name="author" placeholder="Your name" />
				<textarea rows={10} name="body"></textarea>
				{bodyError && (

Review comment (bug):
Just noting that if this is required, it's a user-side bug. Understand if this is just a debug in-progress

Code after:
import { actions, isInputError } from 'astro:actions';
import { useState } from 'react';
import {createLoggerFromFlags} from "../../../../../src/cli/flags.ts";

export function PostComment({
	postId,
	serverBodyError,
}: {
	postId: string;
	serverBodyError?: string;
}) {
	const [comments, setComments] = useState<{ author: string; body: string }[]>([]);
	const [bodyError, setBodyError] = useState<string | undefined>(serverBodyError);
	const [unexpectedError, setUnexpectedError] = useState<string | undefined>(undefined);

	return (
		<>
			<form
				method="POST"
				data-testid="client"
				action={actions.blog.comment}
				onSubmit={async (e) => {
					e.preventDefault();
					const form = e.target as HTMLFormElement;
					const formData = new FormData(form);
					const { data, error } = await actions.blog.comment(formData);
					if (isInputError(error)) {
						return setBodyError(error.fields.body?.join(' '));
					} else if (error) {
						return setUnexpectedError(`${error.code}: ${error.message}`);
					}
					setBodyError(undefined);
					setComments((c) => [data, ...c]);
					form.reset();
				}}
			>
				{unexpectedError && (
					<p data-error="unexpected" style={{ color: 'red' }}>
						{unexpectedError}
					</p>
				)}
				<input type="hidden" name="postId" value={postId} />
				<label htmlFor="author">Author</label>
				<input id="author" type="text" name="author" placeholder="Your name" />
				<textarea rows={10} name="body"></textarea>
				{bodyError && (