Posted at

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 を使うという人は読む必要はありません。

TypeScript + React + Material-UI v3 のスタイル付き Components ガイド



  • withStyles(styles)(Component) という記法ではなくなった(互換性はあるため v4 でも利用可能)

  • 代わりに makeStyles という関数を使ってスタイルを定義する

  • className を props で与えるのではなくコンポーネント内で取得する


Material-UI v4 での記法


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


MyStyledComponent.tsx

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) という煩雑な書き方がなくなりすっきりとした表現になっています。


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 Component Class を使わずに、React 16.8 で導入された React Hooks で書いているドキュメントが多いです。


Props の定義は typeinterface

interface Props {

title?: string;
}

type Props = {

title?: string;
}

最近は type を使うのをよく目にします。

また state を使用する場合でも、 React Hooks を使って、型定義 State を定義しない書き方を多く目にします。

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


まとめ

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

Material-UI v4 is out

https://medium.com/material-ui/material-ui-v4-is-out-4b7587d1e701

Material-UI

https://material-ui.com

React & Redux in TypeScript - Static Typing Guide

https://github.com/piotrwitek/react-redux-typescript-guide