水戸地図(β)

2019年06月04日

TypeScript + Material-UI v4 のスタイル付きコンポーネント作成ガイド

2019年5月下旬に Material-UI v4 が正式にリリースされました。スタイル付きコンポーネントの記法が v3 以前のものと変わったのでメモしておきます。

環境

create-react-app で作成したディレクトリを前提にしています。

参考: TypeScript React Starter

バージョン

- typescript @3.2.2
- @material-ui/core @4.0.2
- react @16.8.6
- react-dom @16.8.6

v3 からの変更点

v3 でのスタイル付きコンポーネントの記法は以下の記事を参照してください。
v4 で初めて Material-UI を使うという人は読む必要はありません。

  • withStyles(styles)(Component) という記法ではなくなった(互換性はあるため v4 でも利用可能)
  • 代わりに makeStyles という関数を使ってスタイルを定義する
  • className を props で与えるのではなくコンポーネント内で取得する

追記(2019/12/17)

以下の記法は、React 16.8 で追加された フック(hook) を使っています。

追記(2020/02/20)

props で変数を渡す方法を追記しました。

追記(2021/09/20)

MUI (Material-UI) v5 がリリースされました。 MUI v5 のスタイリング方法については↓をどうぞ

広告

Material-UI v4 での記法

スタイル付きコンポーネント作成の一例です。

import * as React from 'react';
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';

// スタイルを定義
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      padding: theme.spacing(2)
    },
    title: {
      borderBottom: `2px solid ${theme.palette.primary.main}`
    }
  })
);

// props の型を定義
type Props = {
  title?: string;
}

// コンポーネントを定義
function MyStyledComponent({ title }: Props) {
  // ここでクラス名を取得
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <h4 className={classes.title}>
        {title || 'My Styled Component'}
      </h4>
    </div>
  );
}

// エクスポート
export default MyStyledComponent;

React & Redux in TypeScript - Static Typing Guide を参考にしています。

1. インポート

// JavaScript の場合は createStyles, Theme は不要
import { makeStyles, createStyles, Theme } from "@material-ui/core/styles";

createStyles は型拡大を防ぐ関数で TypeScript のときのみ任意で使用します。
Theme は Material テーマ (theme) の 型定義で、これを使うことでエディタの補助機能が効き入力が楽になります。

2. フック useStyles を定義

// JavaScript の場合は makeStyles(theme => styleObject)で良い
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      padding: theme.spacing(2),
    },
    title: {
      borderBottom: `2px solid ${theme.palette.primary.main}`,
    },
  }),
);

ここで定義した useStylesmakeStyles で定義したスタイルオブジェクトの key と className の組を返すフックになります。

const classes = useStyles();
console.log(classes);
// => Object { root: "useStyles-root-41", title: "useStyles-title-56" }

3. コンポーネントの中で className を取得

function MyStyledComponent({ title }: Props) {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <h4 className={classes.title}>
        {title || 'タイトル'}
      </h4>
    </div>
  );
}

v3 のときは、コンポーネントの props で className を与えていましたが、v4 ではコンポーネントの中でフックを使ってクラス名を取得します。
これによって、コンポーネントの props の型定義 Props を拡張する必要がなくなりました。

したがって、v3 時代の Props extends WithStyles<typeof styles>export default withStyles(styles)(Component) という煩雑な書き方がなくなりすっきりとした表現になっています。

props で変数を渡す方法 (追記)

makeStyles に props で変数を与える方法も存在します。

interface StylesProps {
  isDark: boolean;
}

const useStyles = makeStyles<Theme, StylesProps>((theme: Theme) =>
  createStyles({
    root: {
      padding: theme.spacing(2),
      color: ({ isDark }) => (isDark ? "#ddd" : null),
      backgroundColor: ({ isDark }) => (isDark ? "#222" : null),
    },
    title: {
      borderBottom: `2px solid ${theme.palette.primary.main}`,
    },
  }),
);

function MyComponent({ title, isDark }: Props) {
  const classes = useStyles({ isDark });
  return (
    <div className={classes.root}>
      <h4 className={classes.title}>{title || "タイトル"}</h4>
    </div>
  );
}

v3 以前からv4 への移行の注意点

v3 時代の withStyles(styles)(Component) という書き方は v4 でも可能です。
互換性がない変更点では、padding などを指定する際に使う theme.spacing が関数になりました 。したがって、v3 以前の theme.spacing.unit * 2 という書き方ではエラーを吐いてしまいます。v3 から v4 へ移行するならまず theme.spacing の書き方を変更しましょう。

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      // v3 時代は theme.spacing.unit * 2 という書き方だった
      padding: theme.spacing(2),
    },
  }),
);

補足: React の記法について

ここは Material-UI に関係ないので読み飛ばしても構いません。
React + TypeScript の記法の流行り(?)に関する話題です。最終的には好みの問題になります。

Function Component で constfunction どちらを使うべきか

const MyComponent: React.FC<Props> = ({ title }: Props) => <div>{title}</div>;
export default MyComponent;
function MyComponent({ title }: Props) {
  return <div>{title}</div>;
}
export default MyComponent;

最近は function 記法を目にすることが多いです。特に理由がなければ、React.FC<Props> はもう使わなくていいかもしれません。

また State を使う場合でも、クラス型コンポーネントを使わずに、React 16.8 で導入された フック で書いているドキュメントが多いです。

Props の定義は typeinterface

interface Props {
  title?: string;
}
type Props = {
  title?: string;
};

最近は type を使うのをよく目にします。
また state を使用する場合でも、 フックを使って、型定義 State を定義しない書き方を多く目にします。

最終的には好みの問題なので、自分のやりやすいように書いてください。

まとめ

ここでは手っ取り早く Material-UI v4 でのスタイル付きコンポーネントの記法について書きました。v4 についてもっと知りたい方は下記の Medium を読むと理解が深まるかと思います。

Material-UI
material-ui.com
フックの導入
ja.reactjs.org

Pretty Old Man / No Buses (2019)

広告

2019年06月04日 最終更新日2021年09月20日

TypeScript + Material-UI v4 のスタイル付きコンポーネント作成ガイド

技術記事

Top

水戸地図(β)