第2回 2020年版 ReactのMaterial UI V4の使い方について

# １．概要
ReactのMaterial UI V4の使い方について、基本的な利用方法について学習する。
Material UIを利用して、画面がデザインできるようになることを目標とする。

- 参考：[material-ui公式](https://material-ui.com/)

# 2. 前提条件
## 作業日時
- 2020/2/28

## ソフトウェアのバージョン

| 分類 | ソフトウェア | バージョン |
|:-:|:-:|:-:|
|フレームワーク| [React](https://ja.reactjs.org/)              | 16.13.0 |
|フレームワーク| React-dom                                     | 16.13.0 |
|静的型付け  | [TypeScript](https://www.typescriptlang.org/) | 3.8.3   |
|| Material UI        | v4.9.4  |
|| @material-ui/core  | 4.9.4  |
|| @material-ui/icons | 4.9.1   |


# 3. 事前準備

Material UIを試すためのサンプル用アプリを`create-react-app`で作成する。
`@material-ui/icons`は、表示も早く、綺麗なSVGアイコンを利用するためにインストールする。

```console
$ npx create-react-app material-ui-sample --template typescript
$ cd material-ui-sample/
$ yarn add @material-ui/core @material-ui/icons
```



以下コマンドでローカルにサーバーを立上げ、ブラウザで画面イメージを確認しながら、編集を行う。プログラムを編集すると自動的に画面に変更が反映される。

```console
$ yarn start
```

上記、コマンドで自動的にブラウザが起動するが、ブラウザを閉じてしまった場合などは、 http://localhost:8080/ でアクセスすることが可能。


# 4. サンプルアプリの作成
## App.tsx

既存の`App.tsx`を以下のように修正する。


```tsx:/src/App.tsx
import React from "react";
import './App.css';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import CssBaseline from "@material-ui/core/CssBaseline";
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';

// 独自に作成したコンポーネントのインポート
import MainContent from "./MainContent";
import MyAppBar from "./MyAppBar";

// 独自のテーマを作成する
const theme = createMuiTheme({
  palette: {
    //type: 'dark', // ダークテーマ
    primary: green,
    // primary: red,
  },
  typography: {
    fontFamily: [
      'Noto Sans',
      'sans-serif',
    ].join(','),
    fontSize: 12,
    h1: {
      fontSize: "1.75rem"
    },
    h2: {
      fontSize: "1.5rem"
    },
    h3: {
      fontSize: "1.25rem"
    },
    h4: {
      fontSize: "1.125rem"
    },
    h5: {
      fontSize: "1rem"
    },
    h6: {
      fontSize: "1rem"
    },
  }
});

function App() {

  return (
    <MuiThemeProvider theme={theme}>
        <CssBaseline />
        <MyAppBar />
        <MainContent />
    </MuiThemeProvider >
  );
}

export default App;
```
ソースの冒頭で各種必要なpackageをimportしている。
importの仕方は以下の２つ（`{}`の有無）がある。`{}`無しはdefaultでexportされているものを読み込む。
- `import hoge from hogefuga`
- `import ｛hoge, fuga｝ from hogefuga`

`createMuiTheme`はMaterial UIのテーマを修正するために利用する。`palette.primary`の色を指定するだけで、その色をベースとしたテーマにすることができる。
Themeを適用するためには` <MuiThemeProvider theme={theme}>`のタグで囲う必要がある。その配下でThemeが適用される。

- 参考：[Theming](https://material-ui.com/customization/theming/)


`CssBaseline`は、ブラウザ間の表示差異をなくし、意図通りにスタイリングを行なうためのもの。
ページのマージンが無くなり、背景色がtheme.palette.background.defaultで指定した色になる。

- 参考:[css-baseline](https://material-ui.com/components/css-baseline/)

## MyAppBar.tsx

`MyAppBar.tsx`を`src`配下に新規に作成する。

```tsx:/src/MyAppBar.tsx
import React, { useState } from "react";
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';

import {
  AppBar,
  Toolbar,
  Typography,
  IconButton,
  Avatar,
  List,
  ListItem,
  ListItemIcon,
  ListItemText,
  Drawer,
  Divider,
} from '@material-ui/core';

// Material-UIアイコン取得
import SettingsIcon from '@material-ui/icons/Settings';
import InfoIcon from '@material-ui/icons/Info';
import HomeIcon from '@material-ui/icons/Home';
import MenuIcon from "@material-ui/icons/Menu";

// スタイルを適用する
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    headerLogo: {
      color: "inherit",
      marginRight: 20,
    },
    headerTitleStyle: {
      flexGrow: 1,
      color: "inherit",
    },
    menuButton: {
      color: "inherit",
      padding: '8px',
    },
    avatar: {
      margin: '8px',
    },
    drawerList: {
      width: 200,
      height: "100%"
    },
  }),
);

function MyAppBar() {

  // Drawerの状態
  const [isOpen, setOpen] = useState(false);

  // CSSを適用する。
  const classes = useStyles();

  // Drawerの状態を変更する関数。
  const toggleDrawerNav = () => {
    setOpen(!isOpen);
  };

  const closeDrawerNav = () => {
    setOpen(false);
  };

  return (
      <React.Fragment>
        {/* 上部のバー */}
        <AppBar position='static' aria-label="Global Navi">
          <Toolbar>
            <IconButton onClick={toggleDrawerNav} aria-label="SideMenu">
              <MenuIcon />
            </IconButton>
            <Typography className={classes.headerLogo} variant="subtitle1">My Sample App</Typography>
            <Typography className={classes.headerTitleStyle} variant="subtitle1" >Material UI test</Typography>
            <IconButton className={classes.menuButton} aria-label="Menu">
              <Avatar className={classes.avatar}></Avatar>
            </IconButton>
          </Toolbar>
        </AppBar>
        {/* サイドメニュー */}
        <Drawer open={isOpen} onClose={closeDrawerNav}>
          <div className={classes.drawerList}>
            <List>
              <ListItem button onClick={closeDrawerNav}>
                <ListItemIcon>
                  {<HomeIcon />}
                </ListItemIcon>
                <ListItemText primary={"Home"} />
              </ListItem>
              <ListItem button onClick={closeDrawerNav}>
                <ListItemIcon>
                  {<InfoIcon />}
                </ListItemIcon>
                <ListItemText primary={"Info"} />
              </ListItem>
              <ListItem button onClick={closeDrawerNav}>
                <ListItemIcon>
                  {<SettingsIcon />}
                </ListItemIcon>
                <ListItemText primary={"Setting"} />
              </ListItem>
            </List>
            <Divider />
          </div>
        </Drawer>
      </React.Fragment>
  );
}

export default MyAppBar;
```

`const useStyles = makeStyles((theme: Theme) =>{}`はMateril-uiでCSSを適用するための方法。
＊他にもいくつかCSSを適用する方法はあります。
冒頭でクラス名と適用したいスタイルを宣言します。CSSの記法と多少異なるので注意が必要です。

実際に利用するには、まず、関数の中で`const classes = useStyles();`を呼び出し、スタイルを適用したいコンポーネントに`<Avatar className={classes.avatar}></Avatar>`でクラス名を指定します。

`const [isOpen, setOpen] = useState(false);`ではReact HookのuseStateというものを利用しています。
useStateは`const [<変数名>, <変数に値を設定する関数名>] = useState(<初期値>);`でstate(変数)を宣言することができます。ここでは`Drawer`の状態を表す変数を宣言しています。

Material-uiのコンポーネントは`import {hoge} '@material-ui/core';`でインポートしてから利用します。
アイコンは`import MailIcon from '@material-ui/icons/Mail';`で利用します。

## MainContent.tsx

続いて、`MainContent.tsx`を`src`配下に新規に作成する。


```tsx:/src/MainContent.tsx
import React from "react";
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';

import {
  Badge,
  CircularProgress,
  Paper,
  Button,
  Fab,
  Grid
} from '@material-ui/core';

// Material-UIアイコン取得
import MailIcon from '@material-ui/icons/Mail';
import ShareIcon from '@material-ui/icons/Share';
import ListAlt from '@material-ui/icons/ListAlt';
import PersonAdd from '@material-ui/icons/PersonAdd';
import Lock from '@material-ui/icons/Lock';
import Chat from '@material-ui/icons/Chat';
import Assessment from '@material-ui/icons/Assessment';
import CloudUpload from '@material-ui/icons/CloudUpload';
import AssignmentTurnedIn from '@material-ui/icons/AssignmentTurnedIn';
import AddIcon from '@material-ui/icons/Add';
import EditIcon from '@material-ui/icons/Edit';
import FavoriteIcon from '@material-ui/icons/Favorite';

// スタイルを適用する
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      padding: '10px',
    },
    paper: {
      padding: theme.spacing(2),
      textAlign: 'center',
      '& > *': {
        margin: theme.spacing(3),
      },
    },
  }),
);

function MainContent() {

  // CSSを適用する。
  const classes = useStyles();

  return (
        <Grid container className={classes.root} spacing={3}>
          <Grid item xs={12} justify="center" >
            <Paper variant="outlined" elevation={3} className={classes.paper}>
              <Button variant="contained">Default</Button>
              <Button variant="contained" color="primary">
                Primary
                </Button>
              <Button variant="contained" color="secondary">
                Secondary
                </Button>
              <Button variant="contained" disabled>
                Disabled
                </Button>
              <Button variant="contained" color="primary" href="#contained-buttons">
                Link
                </Button>
            </Paper>
          </Grid>
          <Grid item xs={12} justify="center" >
            <Paper variant="outlined" elevation={3} className={classes.paper}>
              <Fab color="primary" aria-label="add">
                <AddIcon />
              </Fab>
              <Fab color="secondary" aria-label="edit">
                <EditIcon />
              </Fab>
              <Fab disabled aria-label="like">
                <FavoriteIcon />
              </Fab>
            </Paper>
          </Grid>
          <Grid item xs={12} justify="center" >
            <Paper variant="outlined" elevation={3} className={classes.paper}>
              <CircularProgress />
            </Paper>
          </Grid>
          <Grid item xs={12} justify="center" >
            <Paper variant="outlined" elevation={3} className={classes.paper}>
              <Badge badgeContent={4} color="primary">
                <MailIcon fontSize="small" />
              </Badge>
              <Badge badgeContent={3} color="secondary">
                <MailIcon />
              </Badge>
              <Badge badgeContent={2} color="error">
                <MailIcon fontSize="large"/>
              </Badge>
            </Paper>
          </Grid>
          <Grid item xs={12} justify="center" >
            <Paper variant="outlined" elevation={3} className={classes.paper}>
                <ShareIcon />
                <ListAlt />
                <PersonAdd />
                <Lock />
                <Chat />
                <Assessment />
                <CloudUpload />
                <AssignmentTurnedIn />
            </Paper>
          </Grid>
        </Grid>
  );
}

export default MainContent;
```



# 5. 画面イメージ

ここまでで以下のような画面が作成されます。

<img width="1680" alt="Material-uiサンプル.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293373/780ec394-915e-9a95-49d9-d35f7ebc5f0a.png">

# 6. 最後に
今回はMaterial UIを利用した画面のデザイン方法について説明しました。

Material UIを利用することで、画面のデザインが容易になり、新規アプリの開発が格段に早くなるので、是非使ってみてください。

次回は[「Firebaseとの連携方法」](https://qiita.com/tetsurotayama/items/179daca2cacb0caad0c5)について説明をします。

# 7. 関連記事
Reactに関する記事です。

- [第1回 2020年版 Node.js+Reactのインストール](https://qiita.com/tetsurotayama/items/6dd91dc37941fab462a4)
- [第3回 2020年版 React+Firebaseでアプリを作成する](https://qiita.com/tetsurotayama/items/179daca2cacb0caad0c5)
- [第4回 2020年版 既存のウェブサイトに React を追加する](https://qiita.com/tetsurotayama/items/a2e4edd5bc46bc592c32)
- [第5回 2020年版 ReactのRechartsで新型コロナウイルス感染症対策サイトのデータを可視化する](https://qiita.com/tetsurotayama/items/f5eda6dc2bbcd1f71b4e)
- [第6回 2020年版 React+Firebaseで画像のアップロード（その１）](https://qiita.com/tetsurotayama/items/30a0609a0cca5ba5ff0a)
- [第7回 2020年版 React+Firebaseで画像のアップロード（その２）](https://qiita.com/tetsurotayama/items/5129f0cfb21f9ec9b9a0)
- [第8回 2020年版 React+Firebaseで画像のアップロード（その３）](https://qiita.com/tetsurotayama/items/6fb3d3b6e01cac77c85f)
- [第9回 2020年版 ReactにStoryshotsを導入する](https://qiita.com/tetsurotayama/items/dd637e90f4ea008fd878)