React + Material-UI でタグのような複数指定できるフォームを作るには


React + Material-UI で次のような感じのタグフォームを作るのに、
必要なオプション指定の仕方がすぐに分からなかったので簡単にメモ。

※公式ドキュメントはこちら

React Autocomplete component - Material-UI
https://material-ui.com/ja/components/autocomplete/#creatable

Autocomplete API - Material-UI
https://material-ui.com/ja/api/autocomplete/#main-content

前提

"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"react": "^16.13.1",

必要なオプション

Autocomplete Componentに
- multiple ・・複数可にする
- freeSolo ・・自由入力可にする
- clearOnBlur ・・新規選択肢を追加可にする
- onChange={(e,v) => setTags(v)} ・・入力値をuseStateを使ってセットする
- ※追記2020/12/20: こちらのonChangeは 不要でした

TexstField Componentに
- {(e) => {setTags(e.target.value)}} ・・入力値をuseStateを使ってセットする
- {(e,v) => setTags(v.map(tag=>tag.title ? tag.title : tag))} ・・入力値をuseStateを使ってセットする
- ※追記2020/12/20: enterキーで入力した時に取得できる値が異なったので修正しました

コード全体

import { useState } from "react";
import AddIcon from '@material-ui/icons/Add';
import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import IconButton from '@material-ui/core/IconButton';
import TextField from '@material-ui/core/TextField';

const filter = createFilterOptions();

export default function TaskCreateForm(props) {
  const [tags, setTags] = useState([]);
  const [addFormOpen, setAddFormOpen] = useState(false);


  const handleClickAddFormOpen = () => {
    setAddFormOpen(true);
  };

  const handleAddFormClose = () => {
    setAddFormOpen(false);
  };

  const add = () => {
    setTags([]);
    console.log(tags);
    handleAddFormClose();
  }

  return (
    <div>
      <IconButton aria-label="add" color="primary" onClick={handleClickAddFormOpen}>
        <AddIcon />
      </IconButton>

      <Dialog open={addFormOpen} onClose={handleAddFormClose} aria-labelledby="form-dialog-title">
        <DialogContent style={{ minWidth: 400 }}>
          <form className={classes.formRoot} noValidate autoComplete="off">
            <div>
              <Autocomplete
                id="tags"
                size="small"
                options={[]}
                getOptionLabel={(option) => {
                  // Value selected with enter, right from the input
                  if (typeof option === 'string') {
                    return option;
                  }
                  // Add "xxx" option created dynamically
                  if (option.inputValue) {
                    return option.inputValue;
                  }
                  // Regular option
                  return option.title;
                }}
                multiple // 複数可
                freeSolo // 自由入力可
                clearOnBlur // 新規選択肢を追加可
                filterOptions={(options, params) => {
                  const filtered = filter(options, params);
                  // Suggest the creation of a new value
                  if (params.inputValue !== '') {
                    filtered.push({
                      inputValue: params.inputValue,
                      title: params.inputValue,
                    });
                  }
                  return filtered;
                }}
                onChange={(e,v) => setTags(v.map(tag=>tag.title ? tag.title : tag))}
 //★ ←複数のテキストを取得するにはココ必要(※追記修正2020/12/20)
                renderInput={(params) => (
                  <TextField
                    {...params}
                    label="tag"
                    variant="standard"
                    // onChange={(e) => {setTags(e.target.value)}} //★ ←ココは消してOK(※追記2020/12/20)
                  />
                )}
              />
            </div>
          </form>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleAddFormClose} color="primary">
            Cancel
          </Button>
          <Button onClick={add} color="primary" variant="contained">
            Add
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

参考

マテリアルUIからのオートコンプリートコンポーネントの変更の処理
https://qastack.jp/programming/58684579/handle-change-on-autocomplete-component-from-material-ui