#はじめに
React + Typescript + MaterialUIでフロントエンドを作成していたときに実現したいことができなかったので参考程度に投稿します。
##実現できなかった原因
Chipの×ボタンを押してもSelectのメニューが出てきてしまう
##実現したいこと
MaterialUIの公式ドキュメントに書かれていた「Multiple Select(Chip)」でChipの×ボタンからも選択を解除したい!
#結論
Chipコンポーネントに以下プロパティを追加
onMouseDown={(event) => {event.stopPropagation();}}
#前提条件
- typescript,React,MaterialUiについてある程度の知識がある。
- create-react-appでアプリケーションが作成済み
npx create-react-app {your_app_name} --template typescript
- material-uiがインストール済み
npm install @material-ui/core
#Multiple SelectのChipを動かしてみる
まずは何も考えずコードをすべてコピーしてApp.tsx
に貼り付ける。
次にChip以外のところは邪魔なのでChipに関連するものだけを残す。
そうするとコードは以下のようになります。
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
でみると
よさそう!※拡大してます
#Chipの×ボタンを表示する
Chipに×ボタンを表示するにはChipコンポーネントにonDelete
プロパティを追加すればいい。
<Chip
key={value}
label={value}
className={classes.chip}
onDelete={() => { alert(value) }} /> #ここを追加
今回は×ボタンをがクリックされたことを確認するために、alertでクリックしたChipのラベルを表示するようにした。
そして再度npm start
あれ...?何度押しても×ボタンがクリックできずにSelectのメニューが出てきてしまう。
#対処法
Chipコンポーネントに以下プロパティを追加
<Chip
key={value}
label={value}
className={classes.chip}
onDelete={() => { alert(value) }}
onMouseDown={(event) => {event.stopPropagation()}} #ここを追加
/>
今度はきちんとクリックされてアラートが出ていますね。
#Chip削除の処理を追加する
先ほど入れたalertの処理をChip削除(personName内の名前を削除)する処理に変える。
新しくchipDelete
という関数を用意する
const chipDelete = (name: string) => {
setPersonName(personName.filter(value => value !== name))
}
そうするとApp.tsx
のfunction MultipleSelect()
は以下のようになる
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>
);
}
うまくいった!!!!
#終わりに
今思うとこんなにすっとできてしまうなんて。。。
ただ単にコピペするだけではなくてどのような処理が行われていて、どんなオプションのプロパティがあるのかを確認しないといけないですね。