# フォーム

Webアプリに欠かせないフォームを作成してみましょう。

`components`配下に`Form.tsx`を作成しましょう。

`return`で返却する部分はbootstrapのフォームを参考に作成します。

{% embed url="<https://getbootstrap.jp/docs/5.3/forms/overview/>" %}

{% code title="Form.tsx◎" %}

```tsx
export const Form = () => {
  return (
    <form>
      <div className="mb-3">
        <label htmlFor="nameForm" className="form-label">
          名前
        </label>
        <input type="text" className="form-control" id="nameForm" />
      </div>
      <div className="mb-3">
        <label htmlFor="emailForm" className="form-label">
          メールアドレス
        </label>
        <input type="email" className="form-control" id="emailForm" />
      </div>
      <div className="mb-3">
        <label htmlFor="password" className="form-label">
          パスワード
        </label>
        <input type="password" className="form-control" id="password" />
      </div>
      <div className="mb-3 form-check">
        <input
          type="checkbox"
          className="form-check-input"
          id="isMagazineCheck"
        />
        <label className="form-check-label" htmlFor="isMagazineCheck">
          メールマガジンを希望する
        </label>
      </div>
      <button type="submit" className="btn btn-primary">
        送信
      </button>
    </form>
  );
};
```

{% endcode %}

こちらをApp.tsxにて呼び出しましょう。

<pre class="language-tsx" data-title="App.tsx◎"><code class="lang-tsx">...
import { UserList } from "./components/UserList"; 
<strong>import { Form } from "./components/Form"; 
</strong>...
      &#x3C;UserList />
<strong>      &#x3C;Form />
</strong>...
</code></pre>

以下のようなフォームができたかと思います。

<br>

<figure><img src="https://1869761657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcUBbYqol4PMzZJggiMqV%2Fuploads%2FinnpfV1XnXpWwPsUsjvs%2F202312071658.png?alt=media&#x26;token=434982c6-43d0-4bee-b56b-3f3e2714204c" alt=""><figcaption></figcaption></figure>

labelタグの`htmlFor`に関してですが、inputタグの`id`と同じ値を格納すると、label要素をクリックした際に、対象のinputタグにフォーカスされます。

formタグに関してですが、子要素の`type="submit"`のbuttonタグが押された際に、onSubmit属性に渡した処理が走ります。`handleSubmit`という関数を定義して、`onSubmit`に渡しましょう。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx">export const Form = () => {
<strong>  const handleSubmit = () => console.log("送信");
</strong>
  return (
<strong>    &#x3C;form onSubmit={handleSubmit}>
</strong>...
</code></pre>

送信ボタンを押すと、コンソールに送信と出力されるかと思います。またページがリロードされるようになったかと思います。これはformタグのデフォルトの挙動で、自動的に入力内容を送信してページをリロードする処理が走ります。この処理をキャンセルすることができ、onSubmitにてFormEvent型のオブジェクトを受け取ることができるのですが、そのオブジェクトの`preventDefault`メソッドを呼び出すことで、デフォルトの処理をキャンセルすることができます。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx"><strong>import { MouseEvent } from "react";
</strong>
export const Form = () => {
<strong>  const handleSubmit = (event: FormEvent&#x3C;HTMLFormElement>) => {
</strong><strong>    event.preventDefault();
</strong><strong>    console.log("送信");
</strong><strong>  };
</strong>
  return (
    &#x3C;form onSubmit={handleSubmit}>
...
</code></pre>

次に各入力欄に入力したものをstateにて管理していきます。まずはUser型を作成して、User型のstateを定義します。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx">import { MouseEvent, useState } from "react";

<strong>type User = {
</strong><strong>  name: string;
</strong><strong>  email: string;
</strong><strong>  password: string;
</strong><strong>  isMagazine: boolean;
</strong><strong>};
</strong>
export const Form = () => {
<strong>  const [newUser, setNewUser] = useState&#x3C;User>({
</strong><strong>    name: "",
</strong><strong>    email: "",
</strong><strong>    password: "",
</strong><strong>    isMagazine: false,
</strong><strong>  });
</strong>
  const handleSubmit = (event: MouseEvent&#x3C;HTMLFormElement>) => {
    event.preventDefault();
    console.log("送信");
  };
...
</code></pre>

inputタグにて入力された値をnewUserに格納する関数を作成します。inputタグには`onChange`属性を付与することができ、入力があった際に処理が走ります。この`onChange`属性は`ChangeEvent`型を引数に受け取ることができ、この型から入力された値を受け取ることができます。また、inputタグの`name`属性を受け取ることができ、これを利用することで、`newUser`のどのプロパティかを判断することができるため、`name`、`email`、`password`の変更を一つの関数で完結することができます。

関数を作成したら、inputタグに属性を付与していきます。まず`name`にはstateの対応するプロパティ名、`value`にはその対応するstateのプロパティの値、`onChange`には`onChangeNewUser`を渡します。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx">...
<strong>  const onChangeNewUser = (event: ChangeEvent&#x3C;HTMLInputElement>) => {
</strong><strong>    const { name, value } = event.target;
</strong><strong>    setNewUser({ ...newUser, [name]: value });
</strong><strong>  };
</strong>
  const handleSubmit = (event: MouseEvent&#x3C;HTMLFormElement>) => {
    event.preventDefault();
    console.log("送信");
  };


...
        &#x3C;input
<strong>          name="name"
</strong>          type="text"
          className="form-control"
          id="nameForm"
<strong>          value={newUser.name}
</strong><strong>          onChange={onChangeNewUser}
</strong>        />
...
        &#x3C;input
<strong>          name="email"
</strong>          type="email"
          className="form-control"
          id="emailForm"
<strong>          value={newUser.email}
</strong><strong>          onChange={onChangeNewUser}
</strong>        />
  
...
        &#x3C;input
<strong>          name="password"
</strong>          type="password"
          className="form-control"
          id="password"
<strong>          value={newUser.password}
</strong><strong>          onChange={onChangeNewUser}
</strong>        />
...
</code></pre>

newUserの値が変更されたか確認するために、handleSubmitにてnewUserをコンソールに出力するようにしましょう。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx">...
  const handleSubmit = (event: MouseEvent&#x3C;HTMLFormElement>) => {
    event.preventDefault();
<strong>    console.log(newUser);
</strong>  };
...
</code></pre>

上記にて、フォームを入力して送信ボタンを押すと、`newUser`に値が格納されていることが確認できます。

<figure><img src="https://1869761657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcUBbYqol4PMzZJggiMqV%2Fuploads%2FRyKbyKoWEXEXBumOgedo%2F202312071745.png?alt=media&#x26;token=c29c63ad-3b46-4caa-a7ae-00f6cf117df2" alt=""><figcaption></figcaption></figure>

`isMagazine`に関しては工夫が必要で、inputタグのtypeがcheckboxのため、チェックされたかどうかを受け取る必要があり、`event.target.checked`を受け取ることで、チェックされた場合に`true`、外した場合に`false`を受け取ります。そして、`checkbox`の場合のみにcheckedの値を扱うため、`event.target.type`を受け取り、`type`が`checkbox`の場合に、`checked`の値を格納するようにします。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx">...
  const onChangeNewUser = (event: ChangeEvent&#x3C;HTMLInputElement>) => {
<strong>    const { name, value, type, checked } = event.target;
</strong><strong>    setNewUser({ ...newUser, [name]: type === "checkbox" ? checked : value });
</strong>  };
...
</code></pre>

次に、isMagazineを変更するinputタグを以下のように修正します。`type="checkbox"`のものに関しては、`value`ではなく、`checked`という属性に`boolean`型を渡します。

<pre class="language-tsx" data-title="Form.tsx◎"><code class="lang-tsx">...
        &#x3C;input
<strong>          name="isMagazine"
</strong>          type="checkbox"
          className="form-check-input"
          id="isMagazineCheck"
<strong>          checked={newUser.isMagazine}
</strong><strong>          onChange={onChangeNewUser}
</strong>        />
...
</code></pre>

上記にて、`isMagazine`の値も変更できるようになったかと思います。
