ブラウザ組み込みの <form> コンポーネント を使用すると、情報を送信するためのインタラクティブなコントロールを作成できます。

<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>

リファレンス

<form>

情報を送信するためのインタラクティブなコントロールを作成するには、ブラウザ組み込みの <form> コンポーネント をレンダリングします。

<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>

以下の例を参照してください。

プロパティ

<form> は、すべての 共通要素のプロパティ をサポートしています。

action: URLまたは関数です。URLが action に渡されると、フォームはHTMLフォームコンポーネントのように動作します。関数が action に渡されると、関数はフォーム送信を処理します。 action に渡された関数は非同期でもよく、送信されたフォームの フォームデータ を含む単一の引数で呼び出されます。 action プロパティは、<button>、<input type="submit">、または <input type="image"> コンポーネントの formAction 属性でオーバーライドできます。

注意点

  • 関数が action または formAction に渡されると、method プロパティの値に関係なく、HTTPメソッドはPOSTになります。

使用方法

クライアント側でフォーム送信を処理する ...(省略)

フォームの`action` propに関数を渡して、フォームが送信されたときにその関数を実行します。 `formData` は引数として関数に渡されるため、フォームによって送信されたデータにアクセスできます。これは、URLのみを受け入れる従来の HTML action とは異なります。

export default function Search() {
  function search(formData) {
    const query = formData.get("query");
    alert(`You searched for '${query}'`);
  }
  return (
    <form action={search}>
      <input name="query" />
      <button type="submit">Search</button>
    </form>
  );
}

サーバー関数でフォーム送信を処理する ...(省略)

入力と送信ボタンを持つ`<form>`をレンダリングします。サーバー関数(`'use server'`でマークされた関数)をフォームの`action` propに渡して、フォームが送信されたときにその関数を実行します。

サーバー関数を `<form action>` に渡すと、JavaScript が有効になっていない場合やコードが読み込まれる前に、ユーザーがフォームを送信できます。これは、接続速度が遅いユーザー、デバイスが遅いユーザー、または JavaScript を無効にしているユーザーにとって有益であり、URL が `action` prop に渡されたときのフォームの動作と似ています。

非表示のフォームフィールドを使用して、`<form>` の action にデータを提供できます。サーバー関数は、`FormData` のインスタンスとして非表示のフォームフィールドデータと共に呼び出されます。

import { updateCart } from './lib.js';

function AddToCart({productId}) {
async function addToCart(formData) {
'use server'
const productId = formData.get('productId')
await updateCart(productId)
}
return (
<form action={addToCart}>
<input type="hidden" name="productId" value={productId} />
<button type="submit">Add to Cart</button>
</form>

);
}

非表示のフォームフィールドを使用して `<form>` の action にデータを提供する代わりに、`bind` メソッドを呼び出して追加の引数を指定できます。これは、関数に渡される `formData` に加えて、新しい引数(`productId`)を関数にバインドします.

import { updateCart } from './lib.js';

function AddToCart({productId}) {
async function addToCart(productId, formData) {
"use server";
await updateCart(productId)
}
const addProductToCart = addToCart.bind(null, productId);
return (
<form action={addProductToCart}>
<button type="submit">Add to Cart</button>
</form>
);
}

`<form>` がサーバーコンポーネントによってレンダリングされ、サーバー関数が `<form>` の `action` prop に渡されると、フォームはプログレッシブエンハンスメントされます。

フォーム送信中に保留状態を表示する ...(省略)

フォームの送信中に保留状態を表示するには、`<form>` でレンダリングされたコンポーネントで `useFormStatus` Hook を呼び出し、返された `pending` プロパティを読み取ります。

ここでは、`pending` プロパティを使用して、フォームが送信中であることを示します。

import { useFormStatus } from "react-dom";
import { submitForm } from "./actions.js";

function Submit() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}

function Form({ action }) {
  return (
    <form action={action}>
      <Submit />
    </form>
  );
}

export default function App() {
  return <Form action={submitForm} />;
}

`useFormStatus` フックの詳細については、リファレンスドキュメントを参照してください。

フォームデータを楽観的に更新する .. .(省略)

`useOptimistic` Hook は、ネットワークリクエストなどのバックグラウンド操作が完了する前に、ユーザーインターフェイスを楽観的に更新する方法を提供します。フォームのコンテキストでは、この手法はアプリの応答性を向上させるのに役立ちます。ユーザーがフォームを送信すると、サーバーの応答を待って変更を反映する代わりに、インターフェイスは期待される結果で即座に更新されます。

たとえば、ユーザーがフォームにメッセージを入力して「送信」ボタンを押すと、`useOptimistic` フックにより、メッセージが実際にサーバーに送信される前でも、メッセージがすぐに「送信中…」ラベル付きでリストに表示されます。この「楽観的」アプローチは、速度と応答性の印象を与えます。その後、フォームはバックグラウンドでメッセージを実際に送信しようとします。サーバーがメッセージの受信を確認すると、「送信中…」ラベルが削除されます。

import { useOptimistic, useState, useRef } from "react";
import { deliverMessage } from "./actions.js";

function Thread({ messages, sendMessage }) {
  const formRef = useRef();
  async function formAction(formData) {
    addOptimisticMessage(formData.get("message"));
    formRef.current.reset();
    await sendMessage(formData);
  }
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true
      }
    ]
  );

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={formAction} ref={formRef}>
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "Hello there!", sending: false, key: 1 }
  ]);
  async function sendMessage(formData) {
    const sentMessage = await deliverMessage(formData.get("message"));
    setMessages([...messages, { text: sentMessage }]);
  }
  return <Thread messages={messages} sendMessage={sendMessage} />;
}

フォーム送信エラーの処理 ...(省略)

場合によっては、`<form>` の `action` prop によって呼び出される関数がエラーをスローすることがあります。これらのエラーは、`<form>` をエラー境界でラップすることで処理できます。`<form>` の action prop によって呼び出される関数がエラーをスローした場合、エラー境界のフォールバックが表示されます。

import { ErrorBoundary } from "react-error-boundary";

export default function Search() {
  function search() {
    throw new Error("search error");
  }
  return (
    <ErrorBoundary
      fallback={<p>There was an error while submitting the form</p>}
    >
      <form action={search}>
        <input name="query" />
        <button type="submit">Search</button>
      </form>
    </ErrorBoundary>
  );
}

JavaScript を使用せずにフォーム送信エラーを表示する ...

プログレッシブエンハンスメントのために JavaScript バンドルが読み込まれる前にフォーム送信エラーメッセージを表示するには、以下が必要です。

  1. `<form>` がサーバーコンポーネントによってレンダリングされること
  2. `<form>` の action prop に渡される関数がサーバー関数であること
  3. `useActionState` Hook を使用してエラーメッセージを表示すること

`useActionState` は、サーバー関数と初期状態の 2 つのパラメーターを取ります。`useActionState` は、状態変数とアクションの 2 つの値を返します。`useActionState` によって返されるアクションは、フォームの action prop に渡す必要があります。`useActionState` によって返される状態変数は、エラーメッセージの表示に使用できます。`useActionState` に渡されたサーバー関数によって返される値は、状態変数を更新するために使用されます。

import { useActionState } from "react";
import { signUpNewUser } from "./api";

export default function Page() {
  async function signup(prevState, formData) {
    "use server";
    const email = formData.get("email");
    try {
      await signUpNewUser(email);
      alert(`Added "${email}"`);
    } catch (err) {
      return err.toString();
    }
  }
  const [message, signupAction] = useActionState(signup, null);
  return (
    <>
      <h1>Signup for my newsletter</h1>
      <p>Signup with the same email twice to see an error</p>
      <form action={signupAction} id="signup-form">
        <label htmlFor="email">Email: </label>
        <input name="email" id="email" placeholder="react@example.com" />
        <button>Sign up</button>
        {!!message && <p>{message}</p>}
      </form>
    </>
  );
}

`useActionState` のドキュメントで、フォームアクションから状態を更新する方法の詳細をご覧ください。

複数の送信タイプの処理 ...

フォームは、ユーザーが押したボタンに基づいて複数の送信アクションを処理するように設計できます。フォーム内の各ボタンは、`formAction` prop を設定することで、個別のactionまたは動作に関連付けることができます。

ユーザーが特定のボタンをタップすると、フォームが送信され、そのボタンの属性とアクションによって定義された対応するアクションが実行されます。たとえば、フォームはデフォルトで記事をレビュー用に送信しますが、`formAction` が記事を下書きとして保存するように設定された別のボタンを持つ場合があります。

export default function Search() {
  function publish(formData) {
    const content = formData.get("content");
    const button = formData.get("button");
    alert(`'${content}' was published with the '${button}' button`);
  }

  function save(formData) {
    const content = formData.get("content");
    alert(`Your draft of '${content}' has been saved!`);
  }

  return (
    <form action={publish}>
      <textarea name="content" rows={4} cols={40} />
      <br />
      <button type="submit" name="button" value="submit">Publish</button>
      <button formAction={save}>Save draft</button>
    </form>
  );
}

... 共通 (<div> など) ... <input>