LoginSignup
23
11

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-05-02

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

今回は第③弾でReactで一覧テーブル作成する部分をやっていくで

環境構築(Docker/Laravel/React.js/Material-UI)
React側でルーティング設定
③Reactで一覧テーブル作成
seederで作ったDBのデータをReactに渡して一覧に表示
新規登録機能
編集・削除機能

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

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

スクリーンショット 2021-05-02 13.02.46.png
まず表示できるものをコピペしてから、
現場でよく使われる書き方として再利用しやすいコンポーネント分割
データの受け渡しを意識した記述にリファクタリングしながら知識習得していくな。

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

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

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

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;

ビルド

$ make npm-dev

これで一旦localhostにアクセスしたら表示はできるようになったと思うわ

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

makeStylesとcreateStylest

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

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

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

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

//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のコンポーネントを呼び出して使ってる感じ屋根

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/

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>

--------------

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

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

その他style

Home.js
import Paper from '@material-ui/core/Paper';
import purple from '@material-ui/core/colors/purple';

色とかデザイン関連のものも呼び出して使える感じやね。

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

mapは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のなかで
存在する数だけをつくっているな

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

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のコードを変えてていくから差分をわかりやすくするためにコミットしておくで

$ git add .
$ git commit -m "一覧テーブルの表示"

テーブルのボディ部分をデータがバックエンドから渡ってくることを想定した定義にリファクタリング

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

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

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

じゃあrowsを定義するわ

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)していくで

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

Home.js
(旧コード)
-----------------------------------------------
    </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>

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で使う形になったな。

ビルドする

$ make npm-dev

表示確認できたな。

rowの中で更にmapする

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

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として利用している感じやね。

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

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

ビルドする

$ make npm-dev

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

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

スクリーンショット 2021-05-02 16.42.33.png

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

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

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

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

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

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

/conponents/MainTable.jsを作成する

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

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

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では使わなくなったものは切り取って来る。

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

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に残った部分は以下のようになるで

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に変数を渡してあげる必要があるで

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>     
MainTable.js


function MainTable(props) {

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

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

    return (
        <TableContainer component={Paper}>

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

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

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

ビルドする

$ make npm-dev

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

コミットしておこう

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

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

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

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

ほなLGTMよろしゅーやで。

23
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
11