Material UIでProps渡しで色指定できるCustomButtonを作る


Material UIのボタンにはデフォルトで以下の様なレパートリーがあるのだけれど

デフォルトのままではBootstrapと比べて色数が少なく
Material UIにはせっかく豊富なカラーパレットが用意されているのでこれをprops渡しで色指定できるボタンを何とか作れないかと試行錯誤

公式ドキュメントにはCustomized buttonsの作り方が説明されているけれども固定色の為なんとか色指定できる汎用的なコンポーネントにしようと試したところコンパイルエラーの嵐

これを

const ColorButton = withStyles(theme => ({
  root: {
    color: theme.palette.getContrastText(purple[500]),
    backgroundColor: purple[500],
    '&:hover': {
      backgroundColor: purple[700],
    },
  },
}))(Button);

こうしようとしたらNGだった

const ColorButton = (props)=> withStyles(theme => ({
  root: {
    color: theme.palette.getContrastText(props.themeColor[500]),
    backgroundColor: props.themeColor[500],
    '&:hover': {
      backgroundColor: props.themeColor[700],
    },
  },
}))(Button);

色々書き換えて試してみたがうまくいかず

別のテーマ指定の方法を書き換えてみたところ

const theme = createMuiTheme({
  palette: {
    primary: green,
  },
});

function CustomizedButtons() {
  return (
    <>
      <ThemeProvider theme={theme}>
        <Button variant="contained" color="primary">
          Theme Provider
        </Button>
      </ThemeProvider>
    </>
  );
}

const CustomButton = (props) => (
  <ThemeProvider theme={createMuiTheme({palette:{primary:props.themeColor}})}>
    <Button variant="contained" color="primary" className={props.className}>
      {props.label}
    </Button>
  </ThemeProvider>
)

function App() {
  return (
    <>
      <CustomButton label="Hello" themeColor={purple} />
    </>
  );
}

上手くいった

かに見えたけど

違う色を並べてみたところ

function App() {
  return (
    <>
      <CustomButton label="Hello" themeColor={purple} />
      <CustomButton label="Hello" themeColor={green} />
    </>
  );
}

KONOZAMA☆

もう一度withStylesでやる路線に
前の書き換え試行錯誤で以下は

const ColorButton = withStyles(theme => ({
  root: {
    color: theme.palette.getContrastText(purple[500]),
    backgroundColor: purple[500],
    '&:hover': {
      backgroundColor: purple[700],
    },
  },
}))(Button);

このように書き換えられるところまでは分かり
withStyles(customTheme)の戻り値はコンポーネントを引数とする関数
withStyles(customTheme)(Button)の戻り値はコンポーネントそのものという事
何かあと一歩足りない

  const customTheme = theme => ({
    root: {
      color: theme.palette.getContrastText(purple[500]),
      backgroundColor: purple[500],
      '&:hover': {
        backgroundColor: purple[700],
      },
    },
  })
  const CustomButton = withStyles(customTheme)(Button)

もう一度withStylesにpropsを渡す方法がないかQiitaで探してたら

…あった
Material-UIのwithStylesで指定するstylesCreatorの中で親からpropsとして受け取った値を動的に適応させるHOC

あるじゃないか

> const withStylesProps = styles => Component => props => {
>   const Comp = withStyles(styles(props))(Component);
>   return <Comp {...props} />;
> };

jsxってこう書くのか…

というわけで欲しかった方法はこんな感じでした(見易さのためmargin関連省略)

import React from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
import { purple, green, red, blue } from '@material-ui/core/colors';

const CustomButton = (props) => {
  const customTheme = theme => ({
    root: {
      color: theme.palette.getContrastText(props.themeColor[500]),
      backgroundColor: props.themeColor[500],
      '&:hover': {
        backgroundColor: props.themeColor[700],
      },
    },
  })
  const ComponentName = withStyles(customTheme)(Button)
  return <ComponentName {...props} />
}

function App() {
  return (
    <>
      <CustomButton themeColor={green}>Hello</CustomButton>
      <CustomButton themeColor={purple}>Hello</CustomButton>
      <CustomButton themeColor={red}>Hello</CustomButton>
      <CustomButton themeColor={blue}>Hello</CustomButton>
    </>
  );
}

export default App;

見た目はこんな感じ