[簡単]React x LaravelのSPAで作るチュートリアル③(Reactで一覧テーブル編)~map()とコンポーネント分割~

# SPAで作るタスク管理アプリのチュートリアル - 一覧テーブル編

今回は第③弾でReactで一覧テーブル作成する部分をやっていくで
>
①[環境構築(Docker/Laravel/React.js/Material-UI)](https://qiita.com/morry_48/items/abd620f051fb4f36dcc2)
②[React側でルーティング設定](https://qiita.com/morry_48/items/b9287bbd4a7d4d312c65)
③Reactで一覧テーブル作成
④[seederで作ったDBのデータをReactに渡して一覧に表示](https://qiita.com/morry_48/items/1f7e7d74eee666e58379)
⑤[新規登録機能](https://qiita.com/morry_48/items/5b2d80b620b3623a4e33)
⑥[編集・削除機能](https://qiita.com/morry_48/items/261becf645a76eec13cf)


### map()とコンポーネント分割の話が中心やで

### こんな感じで一覧表示するものを作っていくわな。

![スクリーンショット 2021-05-02 13.02.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378609/2f7a933f-6ede-98c4-3f98-5450be923f13.png)
まず表示できるものをコピペしてから、
現場でよく使われる書き方として`再利用しやすいコンポーネント分割`や
`データの受け渡しを意識した記述`にリファクタリングしながら知識習得していくな。






## React.jsで一覧テーブルを表示する

まず表示させてから追って解説してく感じにするな。

今あるjs/Home.jsを下記のようにそっくり入れ替えてOKやで。

```react:Home.js

import React　from 'react';
import { Button, Card } from '@material-ui/core';
import { makeStyles, createStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
import purple from '@material-ui/core/colors/purple';

//スタイルの定義
const useStyles = makeStyles((theme) => createStyles({
    card: {
        margin: theme.spacing(5),
        padding: theme.spacing(3),
    },
    table: {
        minWidth: 650,
      },
    tableHead: {
        backgroundColor: purple['A100'],
    },
}));

//ヘッダーのコンテンツ用の配列定義
const headerList = ['名前', 'タスク内容', '編集', '完了'];

function Home() {
    //定義したスタイルを利用するための設定
    const classes = useStyles();

    return (
        <div className="container">
            <div className="row justify-content-center">
                <div className="col-md-10">
                    <div className="card">
                        <h1>タスク管理</h1>
                        <Card className={classes.card}>
                            {/* テーブル部分の定義 */}
                            <TableContainer component={Paper}>
                                <Table className={classes.table} aria-label="simple table">
                                    {/* ヘッダー部分 */}
                                    <TableHead className={classes.tableHead}>
                                        <TableRow>
                                            {headerList.map((item, index) => (
                                                <TableCell align="center" key={index}>{item}</TableCell>
                                            ))}
                                        </TableRow>
                                    </TableHead>
                                     {/* ボディ部分 */}
                                    <TableBody>
                                        <TableRow>
                                            <TableCell align="center">モーリー</TableCell>
                                            <TableCell align="center">肩トレ</TableCell>
                                            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
                                            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
                                        </TableRow>
                                        <TableRow>
                                            <TableCell align="center">ドンキーコング</TableCell>
                                            <TableCell align="center">バナナ補給</TableCell>
                                            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
                                            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
                                        </TableRow>
                                    </TableBody>
                                </Table>
                            </TableContainer>
                        </Card>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default Home;

```

### ビルド

```php
$ make npm-dev
```
これで一旦localhostにアクセスしたら表示はできるようになったと思うわ

### いきなり貼っつけてなんやんねんって感じやろうから上のコードについてこれから説明行くで


### makeStylesとcreateStylest

```react:Home.js
import { makeStyles, createStyles } from '@material-ui/core/styles';

```

`@material-ui`を使うためにimportしてるね。

makeStylesとcreateStylesはReactで使えるcssをいい感じに当てるために使うもんやと思っといていいと思う。

`importする` → `useStylesを定義` → `コンポーネント内でuseStyles()する` → `classNameに指定する`
って感じで使えるで。
`card`って定義したやつに注目すると下記みたいになってるで

```js
//cardに関係ある部分だけ抜粋
import { makeStyles, createStyles } from '@material-ui/core/styles'; //import

↓

const useStyles = makeStyles((theme) => createStyles({
    card: {                             //useStylesとして定義
        margin: theme.spacing(5),
        padding: theme.spacing(3),
    },

↓

function Home() {
    const classes = useStyles(); //Homeコンポーネント内でclassesとして定義

↓

  <Card className={classes.card}>  //classNameに指定

```

理屈が知りたい人はMaterial-UIの公式ドキュメントをしっかり読めばいいと思うけど、
使っていくうちになれるから、このへんはなんとなく`こんなふうに定義して呼び出すんやなーと思っといたら大丈夫`やと思うわ。

適当に別のスタイル定義してコンポーネントに当てたりしてUI変えてみると理解深まると思うで

### TableのStyleについて

 これもmaterial-UIのコンポーネントを呼び出して使ってる感じ屋根

```js
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
```
これも公式ドキュメントにそれぞれのコンポーネントについてわかりやすく解説してあるから、
コンポーネントごとの詳細な仕様とかはそっち見てもらったらいいと思うわ
https://material-ui.com/ja/components/tables/

```react:Home.js
----------------------------
<Table className={classes.table} aria-label="simple table">
    {/* ヘッダー部分 */}
    <TableHead className={classes.tableHead}>
        <TableRow>
            {headerList.map((item, index) => (
                <TableCell align="center" key={index}>{item}</TableCell>
            ))}
        </TableRow>
    </TableHead>
     {/* ボディ部分 */}
    <TableBody>
        <TableRow>
            <TableCell align="center">モーリー</TableCell>
            <TableCell align="center">肩トレ</TableCell>
            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
        </TableRow>
        <TableRow>
            <TableCell align="center">ドンキーコング</TableCell>
            <TableCell align="center">バナナ補給</TableCell>
            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
        </TableRow>
    </TableBody>
</Table>

--------------
```

構成を抜き出すと下記の構成になってるね。

```react:Home.js
<Table className={classes.table} aria-label="simple table">
    <TableHead>
    </TableHead>
    <TableBody>
    </TableBody>
</Table>
```


### その他style

```react:Home.js
import Paper from '@material-ui/core/Paper';
import purple from '@material-ui/core/colors/purple';
```
色とかデザイン関連のものも呼び出して使える感じやね。

## ヘッダーに使用しているmapメソッドについて理解する

mapはReact使ってると理解必須やし頻繁に出てくるから理解しいくで
ヘッダーに関係あるところだけ抜き出すと下記の様になるな


```react:Home.js

//ヘッダーのコンテンツ用の配列定義
const headerList = ['名前', 'タスク内容', '編集', '完了'];

↓

  {/* ヘッダー部分 */}
    <TableHead className={classes.tableHead}>
        <TableRow>
            {headerList.map((item, index) => (
                <TableCell align="center" key={index}>{item}</TableCell>
            ))}
        </TableRow>
    </TableHead>

```

配列で渡ってくるコンテンツをmap()で回しながら、`TableRow`のなかで
存在する数だけ<TableCell>をつくっているな

#### mapで生成される結果については以下やな(上のコードと見比べてみてや)

```react:Home.js
    <TableRow>            
       <TableCell align="center" key="0">名前</TableCell>
       <TableCell align="center" key="1">タスク内容</TableCell>
       <TableCell align="center" key="2">編集</TableCell>
       <TableCell align="center" key="3">完了</TableCell>
    </TableRow>
```

map()で書くとコードがスッキリするし、数が不確定データが渡ってくることを想定すると
よく使うからmap()が分からん人はしっかり理解しとくといいで。

その上で何やけどテーブルのボディ部分についても現状TableCellを手続き的に書いていってるねんけど、ここも、Laravelから`データを受け取る想定のカタチにするためにリファクタリング`していくな。

### つぎの部分からHome.jsのコードを変えてていくから差分をわかりやすくするためにコミットしておくで

```php
$ git add .
$ git commit -m "一覧テーブルの表示"
```
## テーブルのボディ部分をデータがバックエンドから渡ってくることを想定した定義にリファクタリング

現状のTableRowの中身は下記の通りやね

```react:Home.js
    <TableBody>
        <TableRow>
            <TableCell align="center">モーリー</TableCell>
            <TableCell align="center">肩トレ</TableCell>
            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
        </TableRow>
        <TableRow>
            <TableCell align="center">ドンキーコング</TableCell>
            <TableCell align="center">バナナ補給</TableCell>
            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
        </TableRow>
    </TableBody>
```
`TableBody`の中で`TableRow`が渡ってくる数だけ繰り返されることになるから、rowをまとめる変数として`rows`を定義して

画像で見るとこんなイメージ

![スクリーンショット 2021-05-02 15.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378609/10f9001b-7cbd-92ce-8544-536d1fb23e48.png)

丸した部分のTableCellの中身をオブジェクトにして変数`rows`の要素の一つとして扱う感じやね。

じゃあ`rows`を定義するわ


```react:Home.js
const headerList = ['名前', 'タスク内容', '編集', '完了'];

//headrListの下あたりにrowsを定義する
let rows = [
    {
        name: "モーリー",
        content: "肩トレ",
        editBtn: <Button color="secondary" variant="contained">編集</Button>,
        deleteBtn: <Button color="primary" variant="contained">完了</Button>,
    },{
        name: "ドンキーコング",
        content: "バナナ補給",
        editBtn: <Button color="secondary" variant="contained">編集</Button>,
        deleteBtn: <Button color="primary" variant="contained">完了</Button>,
    },
];
```

ボタンは固定やし渡ってくるデータじゃないから切り出す必要あるのかって思うかもしれんけど、後々テーブル部分をコンポーネントとして切り出したときに、外から渡してあげるほうが再利用性が高いから今回は切り出すことにしてるで。

### 作った配列を`TableBody`で展開(map)していくで

旧コードの部分を下記のように変更してや

```react:Home.jｓ
(旧コード)
-----------------------------------------------
    </TableHead>
    {/* ボディ部分 */}
    <TableBody>
        <TableRow>
            <TableCell align="center">モーリー</TableCell>
            <TableCell align="center">肩トレ</TableCell>
            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
        </TableRow>
        <TableRow>
            <TableCell align="center">ドンキーコング</TableCell>
            <TableCell align="center">バナナ補給</TableCell>
            <TableCell align="center"><Button color="secondary" variant="contained">編集</Button></TableCell>
            <TableCell align="center"><Button color="primary" variant="contained">完了</Button></TableCell>
        </TableRow>
    </TableBody>
  </Table>
```
↓

```react:Home.js
(リファクタリング後コード)
-----------------------------------------------
      </TableHead>
       {/* ボディ部分 */}
       <TableBody>
          {rows.map((row, index) => (
              <TableRow key={index}> //mapで回すときはkeyが必要
                  <TableCell align="center">{row.name}</TableCell>
                  <TableCell align="center">{row.content}</TableCell>
                  <TableCell align="center">{row.editBtn}</TableCell>
                  <TableCell align="center">{row.deleteBtn}</TableCell>
              </TableRow>
          ))}
      </TableBody>
  </Table>
```
mapで使う形になったな。

### ビルドする
```php
$ make npm-dev

```
表示確認できたな。

### rowの中で更にmapする
rowの中でも繰り返しが行われている箇所があるね。
それはTableRowの中で列を見たときにTableCellが繰り返されていることがわかると思う。
これはさっき定義したオブジェクトの部分なんやけど、このオブジェクトもmapしていくことにするな。
TableRowの中身を下記のように変更するで

```React:Home.js
    <TableBody>
        {rows.map((row, index) => (
            <TableRow key={index}>
                {Object.keys(row).map(function(key, i) {
                    return(
                        <TableCell align="center" key={i}>{row[key]}</TableCell>
                    );
                })}
            </TableRow>
        ))}
    </TableBody>

```
ちょっとわかりにくいかもしれへんけど
rowに対してmapして{name, content, editBtn, deleteBtn}をkeyとして利用している感じやね。

それでもよく分からん人は下記の記事参照すればいいと思うわ

https://qiita.com/someone7140/items/0bb71fa5a24124afa505

map()使ってテーブルの中に展開する記述は現場でよく見るからここで理解しておくことを強くおすすめするね。

### ビルドする

```
$ make npm-dev
```
リファクタリングしても表示できることが確認出来たらコミットしておこう


```
$ git add .
$ git commit -m "テーブル部分をmap利用するようにリファクタリング"

```

![スクリーンショット 2021-05-02 16.42.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378609/bb76e70f-7656-0210-d696-5268d5ab6c31.png)

コミットまで出来たらおさらいで、リファクタリング前のコードと見比べてみると復習になると思うで。

データが２つやから恩恵がわからんかもしれんけど、バックエンドからデータが大量に渡される想定で考えたらmapで書いてるほうが扱いやすいはずやわ。


でさらに自分のエディタで変更後のテーブルブブンのソースに着目してほしいねんけど、表示コンテンツが全部変数化されてて固定値が一つもないな。

こーゆー風に表示コンテンツを外からもらう形にすることでテーブル部分だけをコンポーネント化して切り出すことができるねん。

切り出すことで、アプリケーション内の他のページで一覧表を表示したいときに再利用できて
見た目的にも統一感のあるアプリーケーションが作れるようになるで


## テーブル部分をコンポーネントとして切り出す

### /conponents/MainTable.jsを作成する

componentsディレクトリは現状空ディレクトリとして存在するはずやからそこに作っていくで

切り出すのは<TableContainer>の中からで
Home.jsから切り取って以下のように設定するで


```react:MainTable.js

function MainTable() {
    return (
        <TableContainer component={Paper}>
            <Table className={classes.table} aria-label="simple table">
                {/* ヘッダー部分 */}
                <TableHead className={classes.tableHead}>
                    <TableRow>
                        {headerList.map((item, index) => (
                            <TableCell align="center" key={index}>{item}</TableCell>
                        ))}
                    </TableRow>
                </TableHead>
                {/* ボディ部分 */}
                <TableBody>
                    {rows.map((row, index) => (
                        <TableRow key={index}>
                            {Object.keys(row).map(function(key, i) {
                                return(
                                    <TableCell align="center" key={i}>{row[key]}</TableCell>
                                );
                            })}
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
}

export default MainTable;
```

コンポーネントの中身が移動出来たらimport部分とスタイル部分をHome.jsから移してくるで。
HomeとMainTable両方で使ってるものはコピーしてHomeでは使わなくなったものは切り取って来る。

それぞれ↓みたいになるで



```react:MainTable.js
import React　from 'react';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
import purple from '@material-ui/core/colors/purple';
import { makeStyles, createStyles } from '@material-ui/core/styles';


const useStyles = makeStyles((theme) => createStyles({
    card: {
        margin: theme.spacing(5),
        padding: theme.spacing(3),
    },
    table: {
        minWidth: 650,
      },
    tableHead: {
        backgroundColor: purple['A100'],
    },
}));

function MainTable(props) {

    //定義したスタイルを利用するための設定
    const classes = useStyles();

//    --------以下は既に移してきているところ

    return (
        <TableContainer component={Paper}>
```

Home.jsに残った部分は以下のようになるで

```react:Home.js
import React　from 'react';
import { Button, Card } from '@material-ui/core';
import { makeStyles, createStyles } from '@material-ui/core/styles';
import MainTable from '../components/MainTable';

//スタイルの定義
const useStyles = makeStyles((theme) => createStyles({
    card: {
        margin: theme.spacing(5),
        padding: theme.spacing(3),
    },
}));

// ------------- 以下の部分は変更無し

//ヘッダーのコンテンツ用の配列定義
const headerList = ['名前', 'タスク内容', '編集', '完了'];

```

これでコンポーネントの切り出しは出来たで。
ただこれでは動かないから、Home.jsの中でMainTable.jsを使う設定をして
MainTable.jsに変数を渡してあげる必要があるで


```react:Home.js
import React　from 'react';
import { Button, Card } from '@material-ui/core';
import { makeStyles, createStyles } from '@material-ui/core/styles';
import MainTable from '../components/MainTable'; // 追記する

------------------------------------
　　　　　　　　　　　　　　//テーブルを切り出した部分でMainTableコンポーネントを利用し変数を渡す
                        <h1>タスク管理</h1>
                        <Card className={classes.card}>
                            {/* テーブル部分の定義 */}
                            <MainTable headerList={headerList} rows={rows} /> //追記する    
                        </Card>
                    </div>     
```

```react:MainTable.js


function MainTable(props) {

    //定義したスタイルを利用するための設定
    const classes = useStyles();

    //親コンポーネントからpropsで受け取る
    const {headerList, rows} = props;    //追記する部分

    return (
        <TableContainer component={Paper}>

```
Home.jsにMainTableコンポーネントを呼び出す記述を追加して<MainTable />を呼び出す。
その際にheaderList, rowsを渡して上げる必要がある

MainTable.js側では親コンポーネント(Home.js)で渡した変数を`props`として受け取るで。

この辺の知識が浅い人はReactそのものの基礎知識やから、React単体のチュートリアルとかで知識補っといてな。

https://ja.reactjs.org/docs/components-and-props.html

### ビルドする
```php
$ make npm-dev
```

localhostにアクセスして、表示確認できたOKやで

### コミットしておこう

```php
$ git add .
$ git commit -m "MainTableコンポーネントとして切り出す"
```

今回出てきた`map()`や`コンポーネント間のデータの受け渡し`は、React案件では基礎知識になるからわからんかった人はReact単体の学習教材等で知識補ったらええと思うわ。

Material-UIの使い方もマスターしておきたいところやね。


### じゃあ次はいよいよ今回の一覧表にLaravel側からデータを渡して表示する部分をやっていくで。

ほなLGTMよろしゅーやで。