Reactで「react-tag-autocomplete」を使ってタグを設定できるようにしてみた

今回はこんな感じでReactでタグ機能を実装してみようと思います。

ちょうどパッケージがあったので一応載せておきます。

[react-tag-input](https://github.com/i-like-robots/react-tags)

実装も全く難しくありませんので、ぜひ肩の力を抜いてご覧ください。

#パッケージのインストール#

```powershell
npm install --save react-tag-autocomplete
```

``react-dnd``と``react-dnd-html5-backend``はバージョン８を選択しないとエラーが起きるので注意してください。

# 実装

まずは、任意の場所に以下のCSSをコピペしてください。

私の場合は、``assets\styles\tagInput.css``に保管します。

```css
.react-tags {
  background-color: #fff;
  position: relative;
  padding: 6px 0 0 6px;
  border: 1px solid #d1d1d1;
  border-radius: 1px;
  font-size: 1em;
  line-height: 1.2;
  cursor: text;
}

.react-tags.is-focused {
  border-color: #b1b1b1;
}

.react-tags__selected {
  display: inline;
}

.react-tags__selected-tag {
  display: inline-block;
  box-sizing: border-box;
  margin: 0 6px 6px 0;
  padding: 6px 8px;
  border: 1px solid #d1d1d1;
  border-radius: 2px;
  background: #f1f1f1;

  /* match the font styles */
  font-size: inherit;
  line-height: inherit;
}

.react-tags__selected-tag:after {
  content: '\2715';
  color: #aaa;
  margin-left: 8px;
}

.react-tags__selected-tag:hover,
.react-tags__selected-tag:focus {
  border-color: #b1b1b1;
}

.react-tags__search {
  display: inline-block;

  /* match tag layout */
  padding: 7px 2px;
  margin-bottom: 6px;

  /* prevent autoresize overflowing the container */
  max-width: 100%;
  z-index: 1000;
}

@media screen and (min-width: 30em) {
  .react-tags__search {
    /* this will become the offsetParent for suggestions */
    position: relative;
  }
}

.react-tags__search-input {
  /* prevent autoresize overflowing the container */
  max-width: 100%;

  /* remove styles and layout from this element */
  margin: 0;
  padding: 0;
  border: 0;
  outline: none;

  /* match the font styles */
  font-size: inherit;
  line-height: inherit;
}

.react-tags__search-input::-ms-clear {
  display: none;
}

.react-tags__suggestions {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
}

@media screen and (min-width: 30em) {
  .react-tags__suggestions {
    width: 240px;
  }
}

.react-tags__suggestions ul {
  margin: 4px -1px;
  padding: 0;
  list-style: none;
  background: white;
  border: 1px solid #d1d1d1;
  border-radius: 2px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}

.react-tags__suggestions li {
  border-bottom: 1px solid #ddd;
  padding: 6px 8px;
}

.react-tags__suggestions li mark {
  text-decoration: underline;
  background: none;
  font-weight: 600;
}

.react-tags__suggestions li:hover {
  cursor: pointer;
  background: #eee;
}

.react-tags__suggestions li.is-active {
  background: #b7cfe0;
}

.react-tags__suggestions li.is-disabled {
  opacity: 0.5;
  cursor: auto;
}

```

必要な変数や関数をを用意しましょう。

```jsx
import React, { createRef, useState } from 'react'
import ReactTags from 'react-tag-autocomplete'
// CSSの読み込み（適時パスを変えて下さい）
import '@assets/styles/tagInput.css'

const TagInput = (props) => {
  const reactTags = createRef
  const suggestions = [
      { id: '1', name: 'USA' },
  ]
  const [tags, setTags] = useState([]),

  const addTags = useCallback(
    (tag) => {
      const newTags = [].concat(tags, tag)
      setTags(newTags)
    },
    [tags, setTags]
  )

  const deleteTags = useCallback(
    (i) => {
      const newTags = tags.filter((tag) => tag.id !== (i + 1))
      setTags( newTags )
    },
    [tags, setTags]
  )
  return (
    <>
      <ReactTags 
        ref={reactTags}
        tags={tags}
        suggestions={suggestions}
        onAddition={addTags}
        onDelete={deleteTags}
      />
    </>
  )
}

export default TagInput
```

ほぼコピペでOKです。

#新しいタグの追加

また、このままだと``suggestions``に無いタグは選択できない設定になっています。

``suggestions``に無いタグも設定できるようにするには、以下のようにコンポーネントの``props``で``allowNew``を渡してあげましょう。

```
      <ReactTags 
        ref={reactTags}
        tags={tags}
        suggestions={suggestions}
        onAddition={addTags}
        onDelete={deleteTags}
        allowNew
      />
```

強いて言うなら、変えるところは``suggestions``くらいですね。

こんな感じでタグ機能を実装することができます。

以上、「Reactで「react-tag-autocomplete」を使ってタグを設定できるようにしてみた」でした！

Thank you for reading