React + MUI v5 でタグのようなUIを作る

**Gmail メール作成のアドレス表示**のように、入力・選択した文字をタグ表示する方法です。React UIライブラリのMUIで作れると知り試作。共有したい方がいたので、この機会に調べた内容を残すことにしました。ご使用の際は、少しでも調査の時短につながれば嬉しいです。
## 試作品
![スクリーンショット 2023-02-03 ゴール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124753/0c017458-e271-c7de-1683-0c78fe9e0a2c.png)
離島名をタグ表示しました。
GitHub: [rito-marker](https://github.com/Kosuke-Nagamatsu/rito-marker.git)
## 環境
typescript: 4.9.5
react: 18.2.0
react-dom: 18.2.0
mui: 5.11.7

:::note info
mui v5を使用するには次のバージョンが必要です
react: 17.0.0 以上
react-dom: 17.0.0 以上
:::

公式情報
> **Peer dependencies**
> react >= 17.0.0 and react-dom >= 17.0.0 are peer dependencies.

## 方法
1. MUIをインストール
1. Autocompleteコンポーネントを使用
1. タグのカラーを変更

### 1. MUIをインストール

```:npmの場合
npm install @mui/material @emotion/react @emotion/styled
```

```:yarnの場合
yarn add @mui/material @emotion/react @emotion/styled
```

### 2. Autocompleteコンポーネント

[Autocompleteのデモ](https://mui.com/material-ui/react-autocomplete/#multiple-values) を参考にしました。必要なコンポーネントをインポートし、次のようにreturnします。

```tsx:src/components/MarkerCreatingInputField.tsx
import { Autocomplete, TextField } from '@mui/material' //必要なコンポーネントをインポート

// ~~ 省略 ~~

return (

  // ~~ 省略 ~~

  <Autocomplete //importしたコンポーネントを使用
    multiple //複数選択できるようになる --- ①
    freeSolo //任意の入力値を管理できる（デフォルトはオプション選択のみ）
    filterSelectedOptions //選択されたオプションを非表示にする --- ②
    options={islands.map(option => option.name)} //ドロップダウンメニューの項目：文字列の配列
    value={inputValues} //入力欄に表示される値：①のときは文字列の配列、指定しないときは文字列 --- ③
    onChange={handleInputChange} //コールバック関数（オプションを選択か「Enter」を押すとイベントが起きる）： function --- ④
    sx={{
      width: 600,
      display: 'inline-block',
    }}
    renderInput={params => (
      <TextField  //importしたコンポーネント
        {...params}
        variant='standard'
        label='離島マーカーを作る' // --- ⑤
        placeholder='離島名を選択か、入力後に「Enter」でタグが表示。「＋」でマーカーを作成'
        error={validation.error} //エラー状態（trueのときは⑤labelや⑥helperTextが赤色になる）： boolean
        helperText={validation.message} //入力欄の下に表示されるテキスト： node（公式のデモ通り文字列を指定） // --- ⑥
      />
    )}
  />
)
```

**②の補足**
選択されたオプションは非表示になります。
![スクリーンショット 2023-02-03 選択前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124753/d03beb68-89a7-007b-7241-b47c2de105c7.png)
![スクリーンショット 2023-02-03 選択後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124753/3bb39b64-6651-d3a8-0463-dd94c4b9fbd1.png)
ですが、選択後に「＋」ボタンでマーカーを作るようにしたので、その場合は再レンダー時にoptionsの配列で初期化され表示されます。そのため、マーカーを作成した離島は取り除きoptionsに含まないようにしました。

```tsx:src/components/providers/ShapeMarkerProvider.tsx
  // Inputのセレクトオプション。textsにある離島名は削除し表示しない
  const islands = useMemo(
    () => ISLANDS.filter(island => texts.indexOf(island.name) === -1),
    [texts]
  )
```

**④の補足**
コールバック関数（handleInputChange）が受け取る主な引数は、function(event, value) です。なので入力した値は`event.target.value`ではなく`value`で渡されます。また、multipleのときは文字列の配列で、指定しないときは文字列になります。このvalueを③のvalueへセットする流れになります。

### 3. タグのカラーを変更
今回はカラーを変更しませんでしたが、追加で調べました。[2. AutocompleteデモのCodeSandbox](https://codesandbox.io/s/dqtyl3?file=/demo.tsx)を参考に`renderTags` を追加します。図のようにChipコンポーネントを加え、例えば`color='info'`で画像のようにinfoカラーになります。今回はスタイル変更ではなくcolorを指定する簡単な例まででした。

```tsx:src/components/MarkerCreatingInputField.tsx
import { Autocomplete, TextField, Chip } from '@mui/material' //Chipを追加

// ~~ 省略 ~~

<Autocomplete
  // ~~ 省略 ~~
  renderTags={(value: readonly string[], getTagProps) =>
    value.map((option: string, index: number) => (
      <Chip variant='outlined' label={option} color='info' {...getTagProps({ index })} /> //選択した離島名が各optionに渡される
    ))
  }
  // ~~ 省略 ~~
/>
```

![スクリーンショット 2023-02-04 8.51.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124753/cb7c3024-8cd4-2681-8195-1a433f016d6e.png)
面白い名前の島があるんですね。ちなみに村役場さんの公式サイトを見ると、宝島は「海とサンゴと伝説に彩られたロマンの島」のようです:palm_tree:

## 参考
- [MUI公式サイト](https://mui.com/)
- [Autocomplete デモ](https://mui.com/material-ui/react-autocomplete/)
[Autocomplete API](https://mui.com/material-ui/api/autocomplete/)
- [Text Field デモ](https://mui.com/material-ui/react-text-field/)
[TextField API](https://mui.com/material-ui/api/text-field/)
- [Chip デモ](https://mui.com/material-ui/react-chip/)
[Chip API](https://mui.com/material-ui/api/chip/)