LoginSignup
1
0

複数のServer Actionsを使いたい

Last updated at Posted at 2023-10-13

前提

  • Server Actionsが何かという話はしません。
  • 本記事で扱うServer Actionsは、Custom Invocation (startTransitionで非同期関数を挟んで実行すること)ではなく、Progressive Enhancement (formのactionに紐づけて使用するServer Actionsのこと) を対象とします。

モチベーション

formタグのactionには一つのServer Actionsしか紐づけることができない。例えば以下のように、登録のほかにサーバ問い合わせが必要なボタンが存在する場合、少し工夫してあげる必要がある。

スクリーンショット 2023-10-13 211634.png

formActionsに紐づける方法

実は、formAction > action であると、みんな大好きMDNに書いてある。

action: フォーム経由で送信された情報を処理するプログラムの URL。この値は (中略) formaction 属性によって上書きすることが可能です。

ということは、2つのServer Actionsを用意して、それぞれ異なるActionを呼んでやればよいはず。ということでサンプルを以下に示す。

"use client";

import { checkName, register } from "./actions";
import { experimental_useFormState as useFormState } from "react-dom";

export function Form() {
  const [stateCheckName, dispatchCheckName] = useFormState(checkName, "");
  const [stateRegister, dispatchRegister] = useFormState(register, "");

  return (
    <form action={dispatchRegister}> // 登録用Server Actions
      ユーザ名 <input type="text" name="name" />
      <button type="submit" formAction={dispatchCheckName}> // 名前確認用 Serer Actions
        使用可能か判定する
      </button>
      {stateCheckName}
      <br />
      パスワード <input type="password" name="password" />
      <br />
      <button type="submit">登録</button>
      {stateRegister}
    </form>
  );
}

codesandbox版はこちら。

ただこれだと、名前の確認のためだけに不要なパスワードも送られてしまうので、改善の余地はありそう。

おまけ

おまけで、試してみたけどダメだったものを紹介する。2つフォームを作ってそれぞれのフォームにactionを紐づける方法だ。残念ながらフォーム in フォームはできないのでちょっと工夫する。

フォームの中にフォームを入れ子にすることは厳格に禁じられています。

しかし、buttonにはformがあり、これで任意のフォームを指定することができる。

form: この属性によって 要素が の中になくても、同一文書内にある任意の 要素に関連付けることが可能になりました。また、祖先の 要素を上書きすることができます。

これを試した結果が以下のサンプルコードである。しかし、残念ながらdispatchCheckNameのアクションで、nameを取得することはできない。inputは複数のformに紐づくことができないからである。

入力された値が必要のないケースであればこの方法は使えそうである。

"use client";

import { checkName, register } from "../actions";
import { experimental_useFormState as useFormState } from "react-dom";

export function Form() {
  const [stateCheckName, dispatchCheckName] = useFormState(checkName, "");
  const [stateRegister, dispatchRegister] = useFormState(register, "");

  return (
    <div>
      <form action={dispatchRegister}>
        ユーザ名 <input type="text" name="name" />
        <button type="submit" form="2nd"> // formを指定することで、別のformのactionを発火
          使用可能か判定する
        </button>
        {stateCheckName}
        <br />
        パスワード <input type="password" name="password" />
        <br />
        <button type="submit">登録</button>
        {stateRegister}
      </form>
      <form id="2nd" action={dispatchCheckName}></form> // nameが送信されない!!!
    </div>
  );
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0