目的と背景
MUI v5の勉強を進めている。styled-componentsを使って、MUIで実装されているcomponentをカスタマイズできるが、省略記法などの影響か、初心者には読み取りにくいコードだったため、一つずつ調べつつ、初心者でもわかるコードに書き換えてみたいと思った。
該当箇所は以下
https://mui.com/material-ui/customization/how-to-customize/
のDynamic CSSの部分
import * as React from 'react';
import { alpha, styled } from '@mui/material/styles';
import Slider, { SliderProps } from '@mui/material/Slider';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';
interface StyledSliderProps extends SliderProps {
success?: boolean;
}
const StyledSlider = styled(Slider, {
shouldForwardProp: (prop) => prop !== 'success',
})<StyledSliderProps>(({ success, theme }) => ({
width: 300,
...(success && {
color: theme.palette.success.main,
'& .MuiSlider-thumb': {
[`&:hover, &.Mui-focusVisible`]: {
boxShadow: `0px 0px 0px 8px ${alpha(theme.palette.success.main, 0.16)}`,
},
[`&.Mui-active`]: {
boxShadow: `0px 0px 0px 14px ${alpha(theme.palette.success.main, 0.16)}`,
},
},
}),
}));
export default function DynamicCSS() {
const [success, setSuccess] = React.useState(false);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSuccess(event.target.checked);
};
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={success}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Success"
/>
<StyledSlider success={success} defaultValue={30} sx={{ mt: 1 }} />
</React.Fragment>
);
}
特に、以下の部分が分からなかった。
interface StyledSliderProps extends SliderProps {
success?: boolean;
}
const StyledSlider = styled(Slider, {
shouldForwardProp: (prop) => prop !== 'success',
})<StyledSliderProps>(({ success, theme }) => ({
width: 300,
...(success && {
color: theme.palette.success.main,
'& .MuiSlider-thumb': {
[`&:hover, &.Mui-focusVisible`]: {
boxShadow: `0px 0px 0px 8px ${alpha(theme.palette.success.main, 0.16)}`,
},
[`&.Mui-active`]: {
boxShadow: `0px 0px 0px 14px ${alpha(theme.palette.success.main, 0.16)}`,
},
},
}),
}));
調べてみた
まず簡単な部分からやっつける。
interface StyledSliderProps extends SliderProps {
success?: boolean;
}
const StyledSlider = styled(Slider, ...)<StyledSliderProps>(...
ここは、Sliderに新たな値(ここでいうところのsuccess)を渡したいので、SliderPropsをimportした上で、拡張し、新たな型(StyledSliderProps)を付けている。
次にここ。ここが分からなかった。
const StyledSlider = styled(Slider, {
shouldForwardProp: (prop) => prop !== 'success',
})
本当はこう書きたい。styled-componentsの継承の書き方を踏襲すると、こうなるはず。
const StyledSlider = styled(Slider)<StyledSliderProps>(({ success , theme }) => ({
color: theme.palette.success.main,
})
調べてみると、emotionとstyled-componentsで書き方が異なるようではあるが、いずれにせよ、カスタムにpropを追加したいときは、shouldForwardPropというのを指定しないといけないらしい。ここが何をしているのか分からなかった。
次にここ。
...(success && {
color: theme.palette.success.main,
'& .MuiSlider-thumb': {
[`&:hover, &.Mui-focusVisible`]: {
boxShadow: `0px 0px 0px 8px ${alpha(theme.palette.success.main, 0.16)}`,
},
[`&.Mui-active`]: {
boxShadow: `0px 0px 0px 14px ${alpha(theme.palette.success.main, 0.16)}`,
},
ここはsuccessがbooleanで渡ってきており、条件分岐でオブジェクトを返す。スプレッド構文で展開をしている。
なので、successの真偽値に従ってreturnを変えるように書き換えることができる。
const StyledSlider = styled(Slider, ...)
<StyledSliderProps>((props) => {
const { success, theme } = props;
return success
? {
color: theme.palette.success.main,
"& .MuiSlider-thumb": {
"&:hover, &.Mui-focusVisible": {
boxShadow: `0px 0px 0px 8px ${alpha(
theme.palette.success.main,
0.16
)}`,
},
"&.Mui-active": {
boxShadow: `0px 0px 0px 14px ${alpha(
theme.palette.success.main,
0.16
)}`,
},
},
}
: {};
});
ついでにオブジェクトのkeyが[]書きになっていたところを変えた。これも何らかの書き方なのだろうが分からず、、書き換えても問題なく動いたので帰られることはわかった。
まとめ
最終的には下記のコードに変更できた。
emotionベースからstyled-componentsベースに書き換えつつ、
オブジェクトのreturnを省略記法を使わずに書くことで分かりやすくなったように思う。
実際にエラーなく動く。
動くが細かいところがまだ理解が追いついていないため、引き続き調査が必要。。。
const StyledSlider = styled(Slider).withConfig({
shouldForwardProp: (prop) => !["success"].includes(prop),
})<StyledSliderProps>((props) => {
const { success, theme } = props;
return success
? {
color: theme.palette.success.main,
"& .MuiSlider-thumb": {
"&:hover, &.Mui-focusVisible": {
boxShadow: `0px 0px 0px 8px ${alpha(
theme.palette.success.main,
0.16
)}`,
},
"&.Mui-active": {
boxShadow: `0px 0px 0px 14px ${alpha(
theme.palette.success.main,
0.16
)}`,
},
},
}
: {};
});
オブジェクトのkeyを[]でラップするのも意味わからんし、
スプレッド構文と条件分岐を併せた書き方もまだよくわかってない。。