LoginSignup
14
8

More than 3 years have passed since last update.

MATERIAL-UIのMultiple SelectでChipから選択を解除する

Last updated at Posted at 2020-09-09

はじめに

React + Typescript + MaterialUIでフロントエンドを作成していたときに実現したいことができなかったので参考程度に投稿します。

実現できなかった原因

Chipの×ボタンを押してもSelectのメニューが出てきてしまう

実現したいこと

MaterialUIの公式ドキュメントに書かれていた「Multiple Select(Chip)」でChipの×ボタンからも選択を解除したい!
last.gif

結論

Chipコンポーネントに以下プロパティを追加

onMouseDown={(event) => {event.stopPropagation();}}

前提条件

  • typescript,React,MaterialUiについてある程度の知識がある。
  • create-react-appでアプリケーションが作成済み
npx create-react-app {your_app_name} --template typescript
npm install @material-ui/core

Multiple SelectのChipを動かしてみる

まずは何も考えずコードをすべてコピーしてApp.tsxに貼り付ける。
次にChip以外のところは邪魔なのでChipに関連するものだけを残す。
そうするとコードは以下のようになります。

App.tsx
import React from 'react';
import { createStyles, makeStyles, useTheme, Theme } from '@material-ui/core/styles';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    formControl: {
      margin: theme.spacing(1),
      minWidth: 120,
      maxWidth: 300,
    },
    chips: {
      display: 'flex',
      flexWrap: 'wrap',
    },
    chip: {
      margin: 2,
    },
    noLabel: {
      marginTop: theme.spacing(3),
    },
  }),
);

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
      width: 250,
    },
  },
};

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

function getStyles(name: string, personName: string[], theme: Theme) {
  return {
    fontWeight:
      personName.indexOf(name) === -1
        ? theme.typography.fontWeightRegular
        : theme.typography.fontWeightMedium,
  };
}

export default function MultipleSelect() {
  const classes = useStyles();
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
    setPersonName(event.target.value as string[]);
  };

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-mutiple-chip-label">Chip</InputLabel>
        <Select
          labelId="demo-mutiple-chip-label"
          id="demo-mutiple-chip"
          multiple
          value={personName}
          onChange={handleChange}
          input={<Input id="select-multiple-chip" />}
          renderValue={(selected) => (
            <div className={classes.chips}>
              {(selected as string[]).map((value) => (
                <Chip key={value} label={value} className={classes.chip} />
              ))}
            </div>
          )}
          MenuProps={MenuProps}
        >
          {names.map((name) => (
            <MenuItem key={name} value={name} style={getStyles(name, personName, theme)}>
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

そしてこれをnpm startしてhttp://localhost:3000でみると
first.gif
よさそう!※拡大してます

Chipの×ボタンを表示する

Chipに×ボタンを表示するにはChipコンポーネントにonDeleteプロパティを追加すればいい。

Apptsx
<Chip 
key={value} 
label={value} 
className={classes.chip} 
onDelete={() => { alert(value) }} /> #ここを追加

今回は×ボタンをがクリックされたことを確認するために、alertでクリックしたChipのラベルを表示するようにした。
そして再度npm start
second.gif

あれ...?何度押しても×ボタンがクリックできずにSelectのメニューが出てきてしまう。

対処法

Chipコンポーネントに以下プロパティを追加

App.tsx
<Chip 
  key={value} 
  label={value} 
  className={classes.chip} 
  onDelete={() => { alert(value) }}
  onMouseDown={(event) => {event.stopPropagation()}}  #ここを追加
/> 

そして再度npm start
third.gif

今度はきちんとクリックされてアラートが出ていますね。

Chip削除の処理を追加する

先ほど入れたalertの処理をChip削除(personName内の名前を削除)する処理に変える。

新しくchipDeleteという関数を用意する

 const chipDelete = (name: string) => {
    setPersonName(personName.filter(value => value !== name))
  }

そうするとApp.tsxfunction MultipleSelect()は以下のようになる

App.tsx
export default function MultipleSelect() {
  const classes = useStyles();
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
    setPersonName(event.target.value as string[]);
  };
  const chipDelete = (name: string) => {
    setPersonName(personName.filter(value => value !== name))
  };

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-mutiple-chip-label">Chip</InputLabel>
        <Select
          labelId="demo-mutiple-chip-label"
          id="demo-mutiple-chip"
          multiple
          value={personName}
          onChange={handleChange}
          input={<Input id="select-multiple-chip" />}
          renderValue={(selected) => (
            <div className={classes.chips}>
              {(selected as string[]).map((value) => (
                <Chip
                  key={value} label={value} className={classes.chip}
                  onDelete={(event) => chipDelete(value)}
                  onMouseDown={(event) => { event.stopPropagation(); }}
                />
              ))}
            </div>
          )}
          MenuProps={MenuProps}
        >
          {names.map((name) => (
            <MenuItem key={name} value={name} style={getStyles(name, personName, theme)}>
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

そしてnpm start
last.gif

うまくいった!!!!:v:

終わりに

今思うとこんなにすっとできてしまうなんて。。。
ただ単にコピペするだけではなくてどのような処理が行われていて、どんなオプションのプロパティがあるのかを確認しないといけないですね。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8