【@material-ui/styles】コンポーネントに渡せないpropsがあるから分割代入でオブジェクトを分けたよって話

現在React.js、 MaterialUIを習得中のりょです。

わからないことばかりで、調べれな調べるほど沼に沈んでいくここ最近でしたが、今日は沼の底に光る原石を発見したのでここに書き留めまする…。

恐ろしい呪文のようなWarning文

最終的な話は、material-ui/systemを使って用意されたコンポーネントはStyleFunctionがあるから、持つ事ができない属性があるよって話だった。(そんなことに3時間くらい悩んだ。)

沼の入り口:const { color , …other } = props;

以下が私のハマった沼ソースである。demo.jsを参照願いたい。

ちなみにこれはMaterilUIの公式リファレンスのここ(https://material-ui.com/ja/styles/basics/#adapting-the-hook-api)

import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';

const useStyles = makeStyles({
  root: {
    background: (props) =>
      props.color === 'red'
        ? 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)'
        : 'linear-gradient(45deg, #2196F3 30%, #21CBF3 90%)',
    border: 0,
    borderRadius: 3,
    boxShadow: (props) =>
      props.color === 'red'
        ? '0 3px 5px 2px rgba(255, 105, 135, .3)'
        : '0 3px 5px 2px rgba(33, 203, 243, .3)',
    color: 'white',
    height: 48,
    padding: '0 30px',
    margin: 8,
  },
});

function MyButton(props) {
  const { color, ...other } = props;
  const classes = useStyles(props);
  return <Button className={classes.root} {...other} />;
}

MyButton.propTypes = {
  color: PropTypes.oneOf(['blue', 'red']).isRequired,
};

export default function AdaptingHook() {
  return (
    <React.Fragment>
      <MyButton color="red">Red</MyButton>
      <MyButton color="blue">Blue</MyButton>
    </React.Fragment>
  );
}

大体JavaScriptは読めるようになってきた。アロー関数も参考演算子もすんなりと読める。イケるぞ!そう思った矢先のconst { color , …other } = props; 何奴…

…otherは分割代入構文の残余引数構文 つまり余りを全部otherに入れた。

なんだか最近できた仕組みらしい。配列やらオブジェクトをめちゃクソ便利に使える仕組みらしい。

今回は渡されたpropsの中から欲しいものを取り出すために使っていた模様。(むしろいらないものを取り出していた?)

const example =()=>{
  const { color, ...rest} = {color: "red", children: "Red",index:1};
  console.log(color); //red
  console.log(rest); //Object {children: "Red", index: 1}
  console.log(rest.children); //Red
}

なぜ親コンポーネントから渡されたpropsを分割する必要があったのか

分割代入構文の残余引数構文で分けられたということはわかった。問題はなぜ分割する必要があったのか。その鍵が@material-ui/systemで用意されているコンポーネントはStyleFunctionを持つと言うこと。

先ほどのdemo.jsの分割代入構文が使われているところを見る。

function MyButton(props) {
  const { color, ...other } = props;
  const classes = useStyles(props);
  return <Button className={classes.root} {...other} />;
}

実は分割されたcolorは使われておらず、むしろ使われているのは{…others}の方なのである。

<Button />コンポーネントに親からもらったpropsのcolor以外をそのまま渡しているのが

<Button className={classes.root} {...other} />

の部分。ちなみにcolorはmakeStyleの関数で<style />を作るのに使われている。

なぜcolor属性は<Button />コンポーネントに渡されなかったのか…。

なぜなら<Button/>コンポーネントはMaterialUIが用意してくれた便利コンポーネントだから。

MaterialUIが用意している便利コンポーネント(MaterialUI/system)

ご存知の通り MaterialUIは便利なコンポーネントがたくさん。ボタンとかカードとかスライダーとか、そのコンポーネントを使うだけで、すぐにレイアウトできてしまう。

今回で言えば<Button />。

import Button from '@material-ui/core/Button';

ちゃんとにimportまでしてる。

そしてMaterialUIが用意してくれたコンポーネントには”Style Function”が使える。これは何かって言うと、コンポーネントの属性に決められた指定をすると、とってもイージーに見た目を変える事ができる。

例えば<Box/>も用意されたコンポーネント。そして↓で与えられている属性はみんな”Style Function”で決められた属性。属性値(primary.mainとか)もある程度決められていて、これはThemeを参照しにいっている。(Default Theme : https://material-ui.com/ja/customization/default-theme/)

<Box
      color="primary.main"
      bgcolor="background.paper"
      fontFamily="h6.fontFamily"
>

話戻して、なんでわざわざ親からもらったpropsから、color属性だけ取り除いて<Button />コンポーネントに渡したのか。

<Button />コンポーネントにcolor=”red”なんて渡しちゃったら、”red”なんて用意されていないよ!なんだよそれ!ちゃんと決められた値を渡してよ!って怒られちゃうんですねー

結論:colorなんて属性の名前をつけたのが間違いなのだ

この一連の流れで僕はMaterialUI/System 、分割代入、StyleFunctionなどの理解を得る事ができた…

きっとこの公式リファレンスはそういった気づきの原石を見つけさせるためにcolorなんていうややこしい属性をつけて、分割代入なんかして、沼に誘い込んだのだろう。

理解がつながると気持ちいいネ。