Material-UI と styled-components を組み合わせて、React のサイトを怠惰にスタイリングする。

[Redux Hooks](https://react-redux.js.org/api/hooks) と [Redux Starter Kit](https://redux-starter-kit.js.org/) を組み合わせると、かなり楽に redux が導入できるということを先日、ご紹介いたしました。

- [Redux Hooks によるラクラク dispatch & states
](https://qiita.com/Ouvill/items/569384e5c8c7ce78f98e)
- [Redux の記述量多すぎなので、 Redux の公式ツールでとことん楽をする。 ( redux-starter-kit)
](https://qiita.com/Ouvill/items/a76e9cbce569d01f2931)

ロジックの記述が楽になったので、今度はウェブサイト、ウェブアプリの見た目をらくに記述できるようになりましょう。

ということで、今回はスタイリングのお話です。

紹介するのは React 界隈では有名なライブラリ（だと思っている）、[Material-UI](https://material-ui.com/) と [styled components](https://www.styled-components.com/) です。

記事の構成は、まずざっと `Material-UI` と `styled components` の説明をしてから、両者を組み合わせて利用する方法について紹介します。

結構余分な話も入っていますから、本題を見たい人は以下のリンクから飛んでください。

[Material-UI と styled components を一緒に使う](https://qiita.com/Ouvill/items/c6761c32d31ffb11e114#material-ui-と-styled-components-を一緒に使う)

## Material-UI

Material-UI は Google が提唱する Material Design に準拠したコンポーネントライブラリです。

![material-ui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/b42ee11e-3498-1d33-e418-f854860fb202.png)


少しのコードで、マテリアルデザインのコンポーネントをサイトに配置できます。

たとえば、クリックしたら波のエフェクトが発生する、ちょっとかっこいいボタンが以下のようなコードで書けます。

```html
<Button variant="contained">ボタンだよ</Button>
<Button variant="contained" color="primary">ボタンだよ</Button>
```

![mui-button.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/0ccc25e5-d69a-17c7-e375-94b13debcea7.gif)

チェックボックスやテキストエリアもかなり凝ったエフェクトなのに簡単に記述できていい感じです。

- チェックボックス

![mui-checkbox.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/1b92cf42-2727-8077-a3f5-b47446cbdd45.gif)


```html
 <Checkbox checked={check} onChange={handleChange} />
```

- テキストフィールド

![mui-textfield.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/9c1678a0-b62c-484e-4a0c-2c13ed54c032.gif)


```html
<TextField label="Name" value={input} onChange={handleChange}/>
```

※ `input`, `check` は状態の変数、`handleChange` は状態変化のハンドラーメソッドだと思ってください。

たぶん React の書籍をみたら、利用方法が紹介されていることが多いですし、知っている人も多いかと思います。

ということで、各コンポーネントの説明はそちらに譲ります。

（どこか手頃な記事がどこかに転がってないか軽く調べてみましたけど、見当たりませんね……。Material-UI に関する詳しい使いかたの説明記事欲しい人、言ってくださったら書くかもしれません）

### React Hooks を利用したスタイリング - useStyles

Material-UI のコンポーネントだけでもかなり見た目を整えることができますが、微調整するためにはやはり CSS の記述が必要です。

React の CSS の記述方法には、素の CSS、CSS Module、CSS in JS（数多のライブラリ）があります。

Material-UI でも独自の CSS in JS の記述方法が用意されています。

さらに Material-UI は Version 4.0.0 が 2019年 5月 23日にリリースされ、React Hooks の仕組みを取り入れた CSS in JS の記述ができるようになりました。

書籍にはまだ載っていないことでしょうし紹介します。

以下はサンプルコードです。

```jsx
import React from "react";
import { Paper, makeStyles } from "@material-ui/core";

// スタイルの記述をする
const useStyles = makeStyles(theme => ({
  outer: {
    width: "100%",
    height: "100%",
    padding: theme.spacing(4),
    backgroundColor: theme.palette.background.default
  },

  inner: {
    width: "720px",
    minHeight: "420px",

    margin: "0 auto",
    padding: theme.spacing(2),

    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  }
}));

// クラス
const UseStyleSample = props => {
    // useStyles() を呼ぶと上記スタイリングが適応されるユニークなクラスネームが取得できる
  const classes = useStyles();

  return (
    //   各コンポーネントにスタイルをあてる
    <div className={classes.outer}>
      <Paper className={classes.inner}>
        Hooks でクラススタイルが書きやすくなったよ
      </Paper>
    </div>
  );
};

export default UseStyleSample;
```

今回のコードで生成される UI はこんな感じです。

![useStyles.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/58f20813-5c2d-a764-91d6-44e6bb53bff0.png)


### 解説

簡単な解説です。

#### makeStyle()

スタイルを定義するメソッドです

```js
makeStyle(theme) => ({
    // クラスのスタイル
})
```

上記のように theme を引数に取る関数を与えてやると、Material-UI の Theme が利用できます。Theme を利用して色指定を行うと、後々サイトのテーマカラーを変更するときなどに一括で指定できて便利です。

#### useStyles()

スタイルに対応したユニークなクラスネームを取得します。

```js
const classes = useStyles()
```

#### 各コンポーネントで利用

各コンポーネントでは`className={classes.****（スタイル名）}`と指定して、スタイルを適用させます。

```jsx
<div className={classes.outer}>
    // 中身
</div>
```

このように Hooks を利用して、結構わかりやすくスタイルが記述できます。

### useStyles の嬉しいところ

- CSS in JS のお陰で 1つのファイルにコンポーネントの要素、スタイルを固められる
- スタイルの指定が v3 までの HOC(withStyles) を利用したときより、書きやすくなった
- エディターのコード補完も効きやすくなった

### useStyles の辛いところ

- CSS を記述しているのに、maxWidth とスタイルの指定が大文字だったりと、
**オブジェクトスタイル辛い**

### 総括

オブジェクトスタイルで CSS を記述することに我慢すれば、Material-UI は結構使えます。

コンポーネントでボタンやチェックボックス、テキストフィールド、ドロワー……etc など、マテリアルデザインで使えそうなコンポーネントはたいてい用意されていますから。

さあ、いちからコンポーネントを作るなんてことをせず、これで怠惰にサイトデザインしましょう。

……と、ここで終われたら記事書いている私としても楽なんですが（ちょっと疲れてきた）、やっぱり CSS のオブジェクトスタイルが記述しにくいので、別の手段を考えましょう。

だって CSS ジェネレーターで生成した CSS がそのままコピペできないなんて辛いですもん！！（楽したい）

## Styled Components

[Styled Components](https://www.styled-components.com/) は CSS 記法のまま CSS in JS を行うことができるライブラリです。

![styled-components.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/3f0ad63f-5083-e8c5-baac-92e1a3db012a.png)

サンプルコードはこんな感じ。

```jsx
import React from "react";
import styled from "styled-components";

// Title コンポーネントの作成、中身は h1 タグにスタイルを適応したもの
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;
// Wrapper の作成、中身は section タグにスタイルを適応したもの
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;
// 作成した Title と Wrapper はコンポーネントとして利用できる

const HelloWorld = () => (
  <Wrapper>
    <Title>Hello World!</Title>
  </Wrapper>
);

export default HelloWorld;
```

生成される画面は以下のようなもの

![styled-hello.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/14b8fbc9-f92c-e062-d94e-d5a85ea44972.png)

styled-components を利用すると、JavaScript でありながら、CSS の記法でスタイルを定義できます。

そしてクラス名の指定をする必要もありませんし、コンポーネント単位でスタリングしやすいです。

ブラボー。

ざっと記事を探してみましたけど、この辺に詳しい説明が載っています。もちろん一番くわしいのは公式サイトですので、そちらを閲覧することが一番良いかとおもいます。

[これからのReactコンポーネントのスタイリングはstyled-componentsが良さそう](https://qiita.com/kiida/items/ceac662d9a8bdd960499)
[styled-componentsを使ったCSS設計](https://qiita.com/taneba/items/4547830b461d11a69a20)

なぜ styled components は JavaScript なのに CSS 記法が書けるのでしょうか？ それは ECMACScript 6 で利用可能になったテンプレートリテラルを利用しているからです。テンプレートリテラルによって、複数行の文字列が定義できるからです。テンプレートリテラルを知っておくと React のコンポーネントの作成や JS の記述にも役に立つので知識として仕入れておくのもいいと思います。

参考になる記事を貼っておきます。

[JavaScript の テンプレートリテラル を極める！](https://qiita.com/kura07/items/c9fa858870ad56dfec12)

このように styled components を利用することで、スタイリングを簡単に行うことができます。

こいつを使えば、React の CSS in JS で疲労することもないので、これを使っていきましょう。

デザイナーさんが作成した CSS やデザインツールで生成した CSS もそのままコピペできて便利です。

さあこいつで怠惰にスタイリングしましょう。

## Material-UI と styled components を一緒に使う

といって、 styled components はかなり便利なのですが、やっぱりいちからコンポーネントを作るのは面倒くさいので、前述の Material-UI と組み合わせてみましょう。

そうです、Material-UI と styled components は一緒に利用できます。少し前までは一緒に利用できなかったり、TypeScript の型定義がぶっ壊れてたりと散々だったんですが、現在のバージョンでは確実に動作します。

Material-UI の公式もそう言っています。

> The styled() method works perfectly on all of our components.

訳：styled() メソッドは完璧に私達のコンポーネントで動作するぜ。[Style Library Interoperability](https://material-ui.com/guides/interoperability/#styled-components)

使いかたは簡単で、Material-UI のコンポーネントを styled components に食わせてやるだけです。

サンプルコード

```jsx
import React from 'react';
import styled from 'styled-components';
import { Button } from '@material-ui/core';

// Material-UI をカッコで囲んで、styled の引数にしてやる
const StyledButton = styled(Button)`
  background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
  border-radius: 3px;
  border: 0;
  color: white;
  height: 48px;
  padding: 0 30px;
  box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
`;

export default function StyledComponents() {
  return (
    <div>
      <Button>Material-UI</Button>
      <StyledButton>Styled Components</StyledButton>
    </div>
  );
}
```

表示例

![material-styled-button.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/f9220c81-927b-c1be-613c-b339ceaf71cc.png)

時たま styled-component のスタイリングと Material-UI のスタイリングの適用順序が逆になって、うまいことスタイルがあたらないこと場合もあります。

![material-styled-button1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/8c3d8ee3-ea66-2a7d-7703-75c7de8dbff8.png)


そのときは、ウェブサイトのルートコンポーネント付近に Material-UI の `StylesProvider` を配置し、Material-UI のスタイル順序を制御してやります。`injectionFirst` のオプションを与えることで、styled components がスタイルを上書きできます。

方法

以下のライブラリをインストールする

```bash
# with npm
npm install --save @material-ui/styles

# with yarn
yarn add @material-ui/styles
```

そして `StylesProvider` でスタイル適応順序の指定

```jsx
import { StylesProvider } from '@material-ui/styles';

//   ルートコンポーネント付近に配置。App とか Layout コンポーネントの中とか
<StylesProvider injectFirst>
  <App />
</StylesProvider>
```

ということで、Material-UI によってある程度デザインされたコンポーネントを利用しつつ、styled components でお馴染みの CSS を記述できるとお話できました。

ということで、これで怠惰にスタイリングだ。いえーい。

## さらに怠惰にスタイリング

このままでも楽なのですが、もう少し楽しましょう。

### Material-UI と styled components のテーマの共通化

Material-UI も styled components のどちらもテーマシステムが用意されています。プライマリーカラーやセカンダリーカラーを変更するときは、テーマを弄ります。

テーマのお陰でコンポーネントごとに個別で色指定をする必要もなくなり、怠惰にスタイリングできます。

さらに Material-UI と styled components のテーマを共通化しておくともっと楽になります。

共通化するといっても、難しいことはなく、同じテーマを各テーマプロバイダーに与えてやるだけです。

では、その方法について。

まずは theme オブジェクトを生成します。Material-UI の `createMuiTheme` を利用すると、Material-UI のデフォルトテーマから拡張できます。（[デフォルトのテーマ定義はこちらで確認できます](https://material-ui.com/customization/default-theme/)）

たとえば、こんな感じのウェブアプリを作成する場合、色のテーマ設定は以下のようになります。

![color-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/5bf6afde-149b-0563-7c67-7fc38756eff4.png)


画像は[material design palette](https://www.materialpalette.com/light-green/deep-orange)より

コード 

```jsx
import { createMuiTheme } from "@material-ui/core";

const theme = createMuiTheme({
  palette: {
    primary: {
      // 緑色
      main: "#8BC34A",
      dark: "#689F38",
      light: "#DCEDC8"
    },
    secondary: {
      // オレンジ
      main: "#FF5722"
    },
    text: {
      // ちょっと薄い黒
      primary: "#212121",
      secondary: "#757575"
    }
  }
});

export default theme;
```

`createMuiTheme` は結構優秀です。`palette` のなかの `secondary` には `main` の色しか指定していません。ですが`createMuiTheme` がよしなに `secondary.dark` や `secondary.light` の色を生成してくれます。（もちろん、きっちりと個別に色を指定したほうがいいですよ）

さて、theme を作成したので、各テーマプロバイダーに提供してあげます。配置場所はルートコンポーネント付近です。

Material-UI も styled components の両者とも、`ThemeProvider` という名前なので、`as` を利用してエイリアスをつけてます。

```jsx
import React from "react";
import { ThemeProvider as MaterialThemeProvider } from "@material-ui/styles";
import { ThemeProvider as StyledThemeProvider } from "styled-components";
import theme from "./Theme";

const App = props => {
  return (
    <>
      <MaterialThemeProvider theme={theme}>
        <StyledThemeProvider theme={theme}>
          <>
                <header />
                <main />
                <footer />
          </>
        </StyledThemeProvider>
      </MaterialThemeProvider>
    </>
  );
};

export default Layout;
```

これで theme が利用可能になりました。

テーマを利用しながら、styled components でとあるコンポーネントの色を指定する場合、以下のようにします。

```jsx
import styled from "styled-components"

// props.theme のなかにテーマが格納されている
const StyledDiv = styled.div`
  color: ${props => props.theme.palette.primary.main};
`
```

※補足

TypeScript を利用している場合、さらに設定が必要となります。theme の型が指定されていないためです。

`styled.d.ts` というファイルを作成し、styled-components の型を拡張します。

`src/types/styled.d.ts`

```jsx

// import original module declarations
import "styled-components"
import { Theme } from "@material-ui/core"

// and extend them!
declare module "styled-components" {
  export interface DefaultTheme extends Theme {
    //   追加でテーマを拡張する場合、この中に定義をかく。
    borderRadius: string
  }
}
```

TypeScript のお陰で補完がさらに利くようになって便利です。参考:[Create a declarations file](https://www.styled-components.com/docs/api#create-a-declarations-file)

テーマシステムによってさらに怠惰にスタイリングできりようになりました。

### styled-components のエディタープラグイン

styled-components ではシンタックスハイライトが動作するように、Atom、Visual Studio Code、WebStorm といった各エディターのプラグインが用意されています。

[Syntax Highlighting](https://www.styled-components.com/docs/tooling#syntax-highlighting)

こいつを VS Code に入れるとハイライトのほかに、コード補完も効き便利なのでインストールしています。

![syntax.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77617/86992ff3-8858-5b83-90f0-9c917b1b4079.gif)

## 復習

長々と説明してきたので復習がてら整理します。

ライブラリのインストール

```bash
npm install --save @material-ui/core @material-ui/styles styled-components
```

今回説明してきたことのソースコード全体。

`src/theme.js`: theme の作成

```js
import { createMuiTheme } from "@material-ui/core";

const theme = createMuiTheme({
  palette: {
    primary: {
      main: "#8BC34A",
      dark: "#689F38",
      light: "#DCEDC8"
    },
    secondary: {
      main: "#FF5722"
    },
    text: {
      primary: "#212121",
      secondary: "#757575"
    }
  }
});

export default theme;
```

`src/App.js` : ルートコンポーネント付近にテーマプロバイダーを設置

```jsx
import React from "react";
import {
  ThemeProvider as MaterialThemeProvider,
  StylesProvider
} from "@material-ui/styles";
import styled, {
  ThemeProvider as StyledThemeProvider
} from "styled-components";
import theme from "./theme";

const App = () => (
    <StylesProvider injectFirst>
      <MaterialThemeProvider theme={theme}>
        <StyledThemeProvider theme={theme}>
            // ここからメインのコンポーネントを記載する
            // <Main></Main> とか
        </StyledThemeProvider>
      </MaterialThemeProvider>
    </StylesProvider>
)
```

`src/components/StyledComponents.js`: Material-UI のコンポーネントを styled-components で装飾する。

```jsx
import React from 'react';
import styled from 'styled-components';
import { Button } from '@material-ui/core';

// Material-UI をカッコで囲んで、styled の引数にしてやる
const StyledButton = styled(Button)`
  background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
  border-radius: 3px;
  border: 0;
  color: white;
  height: 48px;
  padding: 0 30px;
  box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
`;

export default function StyledComponents() {
  return (
    <div>
      <Button>Material-UI</Button>
      <StyledButton>Styled Components</StyledButton>
    </div>
  );
}
```

以上です。これで快適に React のスタイリングができます。

## 蛇足

### その1

ちなみに、蛇足ですが、[emotion](https://emotion.sh/docs/introduction) というスタイリングライブラリも便利です。個人的にはこちらも結構好きです。styled-components の機能も取り入れながら、その他拡張も行ったライブラリです。

この辺がわかりやすいかもです。

[CSS-in-JSのライブラリとして「emotion」を選択している理由
](https://qiita.com/__sakito__/items/d240840eef7123f62acf)


### その2

Material-UI は v4 になってから、styled-components のように記述するメソッドが追加されました。ですが、若干機能が劣っていて使いにくいです。styled-components もしくは emotion を使ったほうが良いです。

## まとめ

Material UI と styled-components についてでした。

これで怠惰にスタイリングしていきましょう。