複数のServer Actionsを使いたい

## 前提
- [Server Actions](https://nextjs.org/docs/app/api-reference/functions/server-actions)が何かという話はしません。
- 本記事で扱うServer Actionsは、[Custom Invocation](https://nextjs.org/docs/app/api-reference/functions/server-actions#invocation) (startTransitionで非同期関数を挟んで実行すること)ではなく、[Progressive Enhancement](https://nextjs.org/docs/app/api-reference/functions/server-actions) (formのactionに紐づけて使用するServer Actionsのこと) を対象とします。

## モチベーション

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

![スクリーンショット 2023-10-13 211634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141888/e042c9b5-0248-f59f-81c9-f571603cf29f.png)

## formActionsに紐づける方法

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


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

https://developer.mozilla.org/ja/docs/Web/HTML/Element/form#%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E9%80%81%E4%BF%A1%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E5%B1%9E%E6%80%A7

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

```tsx
"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版はこちら。

https://codesandbox.io/p/sandbox/multi-server-actions-gmc8tj


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

## おまけ

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

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

https://developer.mozilla.org/ja/docs/Learn/Forms/How_to_structure_a_web_form


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

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

https://developer.mozilla.org/ja/docs/Web/HTML/Element/button#form

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

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


```tsx
"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>
  );
}
```