Reactで排他的なチェックボックスを実装する

#はじめに
訳あって、Reactで排他的なチェックボックスを実装する必要がありました。
ここでいう「排他的」は、グループにつき一個しかチェックできないこと（単一選択）を指しています。
別題（というかタイトル候補）は、「Reactでラジオボタンのようなチェックボックスを実装する」です。

コピペできる全コード付き + 解説ありなので、
初心者でも分かりやすい内容になっていると思います。

#作成したチェックボックス
というわけで、2種類のチェックボックスを作成しました。
一つ目は必ず一つを選択しなければならない、チェックが外せないチェックボックスです。
二つ目はチェックが外せます。チェックの数は0か1ですね。
useStateでチェックの値を管理しています。

片方だけを読んでも分かるように、同じような構成で書いていまして、
重複もしくは全く一緒な内容は結構あります。
2種類とも読んでくださる方には申し訳ないですが、ご了承ください。


# チェックを外せないパータン

### 完成イメージ
##### 初期状態
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/bafb4084-5b9d-d748-98b4-56e6f49d81b9.png)
##### 選択後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/813f1a0e-10fb-18dd-2ca8-c70e0471900c.png)


### 全体コード

```jsx
import React, { useState } from "react";

const MyForm = () => {
  const initializedData = {
    themeColor: "default"
  };

  const [data, setData] = useState(initializedData);

  const handleThemeColorChange = (e) => {
    const newData = { ...data, themeColor: e.target.value };
    setData(newData);
  };

  return (
    <form>
      テーマ：
      <input
        type="checkbox"
        id="themeDeafault"
        value="default"
        checked={data.themeColor === "default"}
        onChange={handleThemeColorChange}
      />
      <label htmlFor="themeDeafault">デフォルト　</label>
      <input
        type="checkbox"
        id="themeRibbon"
        value="ribbon"
        checked={data.themeColor === "ribbon"}
        onChange={handleThemeColorChange}
      />
      <label htmlFor="themeRibbon">リボン　</label>
      <input
        type="checkbox"
        id="themeDog"
        value="dog"
        checked={data.themeColor === "dog"}
        onChange={handleThemeColorChange}
      />
      <label htmlFor="themeDog">犬　</label>
      <input
        type="checkbox"
        id="themeNature"
        value="nature"
        checked={data.themeColor === "nature"}
        onChange={handleThemeColorChange}
      />
      <label htmlFor="themeNature">自然　</label>
    </form>
  );
};
```

### 解説

#### 値の状態管理

```jsx
  // データの初期値を定義
  const initializedData = {
    themeColor: "default"
  };
  
  // hooksで状態管理する
  const [data, setData] = useState(initializedData);

```

チェックボックスだけではなく、フォーム内に複数のデータがあるイメージだったので、
まずはフォーム全体を`initializedData`とします。
そして、今回のチェックボックスの値として、中に`themeColor`を追加します。
必ずどれか選択しているとのことなので、
初期状態として、`default`（画面上の「デフォルト」）を選択させます。

準備が終わったら、hooksのuseStateを使って、
dataとsetDataを定義します。

#### HTML

```jsx
  <input
    type="checkbox"
    id="themeRibbon"
    value="ribbon"
    checked={data.themeColor === "ribbon"}
    onChange={handleThemeColorChange}
  />
  <label htmlFor="themeRibbon">リボン　</label>
```

4つのチェックボックスがありますが、
作りがまったく一緒なので、2番目の「リボン」で説明します。

上から順に説明しますので、まずは`<input>`からですね。
**type**は入力欄の種別を定義できます。`type="checkbox"`でチェックボックスを作ります。
**id**はHTML部品の識別子になります。ページの中に、idを重複させない（一意的である）のが基本です。
**value**にはチェックボックスが表している値を入れます。（詳細後述）
**checked**がtrueの場合、チェックされる状態になります。falseの場合、チェックが外している状態になります。
今回は右側を`{data.themeColor === "ribbon"}`にすることによって、
動的に`data.themeColor`の値によってチェックボックスの状態を変化させることができます。
（`"ribbon"`だったら、チェックされる。`"ribbon"`以外の値だったら、チェックされない。）
**onChange**に`handleThemeColorChange`という関数を入れました。（内容後述）
入力が変更される際に上記の関数を実行するようになります。
チェックボックスの場合、クリック = 値の変化（チェックする／チェックを外す）というところがあるため、
クリックされる度にonChangeは発火します。

次には`<label>`です。
**htmlFor**に`<input>`の**id**と同じ値を入れることによって、ラベルとチェックボックスを関連付けることができます。
この状態でラベルの「リボン」をクリックすると、チェックボックスがクリックされた時と同様の動きになります。

ちなみに、[htmlFor](https://ja.reactjs.org/docs/dom-elements.html#htmlfor)はReactの書き方になります。
通常のHTML（Reactビルド後）だと、以下のようになります。

```html
<label for="themeRibbon">リボン　</label>
```

こうなっている理由について、上記のリンクから引用します。
> for は JavaScript での予約語であるため、React 要素では代わりに htmlFor を使用します。


#### onChangeイベント
先ほど、`<input>`の**onChange**に`handleThemeColorChange`という関数を入れました。
こちらの関数です。

```jsx
  const handleThemeColorChange = (e) => {
    const newData = { ...data, themeColor: e.target.value };
    setData(newData);
  };
```

関数全体はアロー関数で書いており、**function**を使った書き方はこうです。

```jsx
  // 関数式の書き方
  function handleThemeColorChange(e) {
    const newData = { ...data, themeColor: e.target.value };
    setData(newData);
  };
```

また、TypeScriptの方のために、eの型も記載しておきます。

```ts
e: React.ChangeEvent<HTMLInputElement>
```

ここからは関数の中身を説明します。

**一行目**

```jsx
    // ストアのデータを更新するために、新しい値を使って新しいデータを作成
    const newData = { ...data, themeColor: e.target.value };
```
`...data`：スプレッド構文で既存のデータを展開する。
`, themeColor: `：newData.themeColorの値を定義する際の左側。
`e.target.value`：`<input>`の**value**にいれた値を取得。

```js
 // リボンのチェックボックス（value="ribbon"）がクリックされた際
 console.log(e.target.value === "ribbon");  // true
```

**二行目**

```jsx
    // ストア更新
    setData(newData);
```
setDataに`newData`を渡すことによって、ストアのdataを書き換えることができます。



# チェックを外せるパータン

### 完成イメージ
##### 初期状態
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/0ab2c3d8-35b1-036d-7b8a-1155b5bed9c0.png)
##### 選択後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/ed489de5-872e-9f92-1f04-3650a0f8eca6.png)



### 全体コード
```jsx
import React, { useState } from "react";

const MyForm = () => {
  const initializedData = {
    notifyFrequency: "",
  };

  const [data, setData] = useState(initializedData);

  const handleFrequencyChange = (e) => {
    const newValue = (e.target.value === data.notifyFrequency) ? "" : e.target.value;
    const newData = { ...data, notifyFrequency: newValue };
    setData(newData);
  };

  return (
    <form>
      通知頻度：
      <input
        type="checkbox"
        id="notifyAlways"
        value="always"
        checked={data.notifyFrequency === "always"}
        onChange={handleFrequencyChange}
      />
      <label htmlFor="notifyAlways">常時　</label>

      <input
        type="checkbox"
        id="notifyDaily"
        value="daily"
        checked={data.notifyFrequency === "daily"}
        onChange={handleFrequencyChange}
      />
      <label htmlFor="notifyDaily">毎日　</label>

      <input
        type="checkbox"
        id="notifyWeekly"
        value="weekly"
        checked={data.notifyFrequency === "weekly"}
        onChange={handleFrequencyChange}
      />
      <label htmlFor="notifyWeekly">毎週　</label>

      <input
        type="checkbox"
        id="notifyMonthly"
        value="monthly"
        checked={data.notifyFrequency === "monthly"}
        onChange={handleFrequencyChange}
      />
      <label htmlFor="notifyMonthly">毎月　</label>
    </form>
  );
};
```

### 解説

#### 値の状態管理

```jsx
  // データの初期値を定義
  const initializedData = {
    notifyFrequency: ""
  };
  
  // hooksで状態管理する
  const [data, setData] = useState(initializedData);

```

チェックボックスだけではなく、フォーム内に複数のデータがあるイメージだったので、
まずはフォーム全体を`initializedData`とします。
そして、今回のチェックボックスの値として、中に`notifyFrequency`を追加します。
初期状態として、何も選択されていないことを表す`""`（空文字列）にしました。

準備が終わったら、hooksのuseStateを使って、
dataとsetDataを定義します。

#### HTML

```jsx
  <input
    type="checkbox"
    id="notifyDaily"
    value="daily"
    checked={data.notifyFrequency === "daily"}
    onChange={handleFrequencyChange}
  />
  <label htmlFor="notifyDaily">毎日　</label>
```

4つのチェックボックスがありますが、
作りがまったく一緒なので、2番目の「毎日」で説明します。

上から順に説明しますので、まずは`<input>`からですね。
**type**は入力欄の種別を定義できます。`type="checkbox"`でチェックボックスを作ります。
**id**はHTML部品の識別子になります。ページの中に、idを重複させない（一意的である）のが基本です。
**value**にはチェックボックスが表している値を入れます。（詳細後述）
**checked**がtrueの場合、チェックされる状態になります。falseの場合、チェックが外している状態になります。
今回は右側を`{data.notifyFrequency === "daily"}`にすることによって、
動的に`data.notifyFrequency`の値によってチェックボックスの状態を変化させることができます。
（`"daily"`だったら、チェックされる。`"daily"`以外の値だったら、チェックされない。）
**onChange**に`handleFrequencyChange`という関数を入れました。（内容後述）
入力が変更される際に上記の関数を実行するようになります。
チェックボックスの場合、クリック = 値の変化（チェックする／チェックを外す）というところがあるため、
クリックされる度にonChangeは発火します。

次には`<label>`です。
**htmlFor**に`<input>`の**id**と同じ値を入れることによって、ラベルとチェックボックスを関連付けることができます。
この状態でラベルの「毎日」をクリックすると、チェックボックスがクリックされた時と同様の動きになります。

ちなみに、[htmlFor](https://ja.reactjs.org/docs/dom-elements.html#htmlfor)はReactの書き方になります。
通常のHTML（Reactビルド後）だと、以下のようになります。

```html
<label for="notifyDaily">毎日　</label>
```

こうなっている理由について、上記のリンクから引用します。
> for は JavaScript での予約語であるため、React 要素では代わりに htmlFor を使用します。



#### onChangeイベント
先ほど、`<input>`の**onChange**に`handleFrequencyChange`という関数を入れました。
こちらの関数です。

```jsx
  const handleFrequencyChange = (e) => {
    const newValue = (e.target.value === data.notifyFrequency) ? "" : e.target.value;
    const newData = { ...data, notifyFrequency: newValue };
    setData(newData);
  };
```

関数全体はアロー関数で書いており、**function**を使った書き方はこうです。

```jsx
  // 関数式の書き方
  function handleFrequencyChange(e) {
    const newValue = (e.target.value === data.notifyFrequency) ? "" : e.target.value;
    const newData = { ...data, notifyFrequency: newValue };
    setData(newData);
  };
```

また、TypeScriptの方のために、eの型も記載しておきます。

```ts
e: React.ChangeEvent<HTMLInputElement>
```

ここからは関数の中身を説明します。

**一行目**

```js
    // notifyFrequencyに入れるための新しい値を取得
    const newValue = (e.target.value === data.notifyFrequency) ? "" : e.target.value;
```
`e.target.value`：`<input>`の**value**にいれた値を取得。
`data.notifyFrequency`：既存のnotifyFrequencyを取得。

```js
 // 毎日のチェックボックス（value="daily"）がクリックされた際
 console.log(e.target.value === "daily");  // true
```

外せるチェックボックスの場合、チェック済みだったら外す、そうでなければチェックするというロジックになります。
そのため、まずは「チェック済みか」を知る必要があります。

```js
 (e.target.value === data.notifyFrequency)
```
この条件式はまさにそのためです。
両者を比較して、同じ場合（=チェック済み）はtrue、違う場合（=チェックされていない）はfalseになります。
後は三項演算子を使い、
`newValue`に`""`もしくは`e.target.value`を代入して宣言します。

**二行目**

```jsx
    // ストアのデータを更新するために、新しい値を使って新しいデータを作成
    const newData = { ...data, notifyFrequency: newValue };
```
`...data`：スプレッド構文で既存のデータを展開する。
`, notifyFrequency: `：newData.notifyFrequencyの値を定義する際の左側。
`newValue`：新しい値。

**三行目**

```jsx
    // ストア更新
    setData(newData);
```
setDataに`newData`を渡すことによって、ストアのdataを書き換えることができます。

#さいごに
簡単な内容ではありますが、ついつい説明を足したくなります（汗）
長くなってしまいました。
ご質問やご指摘はいつでも受け付けますので、
コメントや編集リクエストをよろしくお願いします。

横並びしている4つのチェックボックスの作りがまったく一緒なので、
工夫すればとループで作成することもできます。
興味のある方はぜひ挑戦してみてください。