2019年06月04日
TypeScript + Material-UI v4 のスタイル付きコンポーネント作成ガイド
2019年5月下旬に Material-UI v4 が正式にリリースされました。スタイル付きコンポーネントの記法が v3 以前のものと変わったのでメモしておきます。
環境
create-react-app
で作成したディレクトリを前提にしています。
バージョン
- 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}`,
},
}),
);
ここで定義した useStyles
は makeStyles
で定義したスタイルオブジェクトの 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 で const
と function
どちらを使うべきか
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>
はもう使わなくていいかもしれません。
- React 公式ドキュメント =>
function
- React & Redux in TypeScript - Static Typing Guide =>
function
- Material-UI ドキュメント =>
function
また State を使う場合でも、クラス型コンポーネントを使わずに、React 16.8 で導入された フック で書いているドキュメントが多いです。
Props
の定義は type
か interface
か
interface Props {
title?: string;
}
type Props = {
title?: string;
};
最近は type
を使うのをよく目にします。
また state を使用する場合でも、 フックを使って、型定義 State
を定義しない書き方を多く目にします。
最終的には好みの問題なので、自分のやりやすいように書いてください。
まとめ
ここでは手っ取り早く Material-UI v4 でのスタイル付きコンポーネントの記法について書きました。v4 についてもっと知りたい方は下記の Medium を読むと理解が深まるかと思います。
Pretty Old Man / No Buses (2019)
Material-UI
Material-UI (MUI)はGoogleのマテリアルデザインをReactで実装するUIライブラリです。
https://mui.com/