Masayan tech blog.

  1. ブログ記事一覧>
  2. Laravelで部分的にReactコンポーネントを使用する際のTips

Laravelで部分的にReactコンポーネントを使用する際のTips

公開日

環境

  • windows10
  • Laravel 8.x
  • PHP 8.x
  • node v14.17.4
  • npm 6.14.14
  • VsCode
  • gitbash 2.32.0.1

動作イメージ

画面はこんな感じです

Tips一覧

  • laravelのbladeファイルにreactコンポーネントを設置して表示する方法
  • laravelのbladeファイルから変数をreactコンポーネントに渡す方法
  • react側のformでcsrf_tokenを設置する方法

Tips詳細

laravelのbladeファイルにreactコンポーネントを設置して表示する方法

ポイントは以下です

  • bladeファイル内でapp.blade.phpを読み込みつつ、中身はsectionディレクティブで展開する
  • app.blade.phpでは、最終的にバンドルされたapp.jsを読み込んでいるので、app.blade.phpを読み込むことでjsをロードすることができる
  • reactコンポーネントをレンダリングしたい箇所のidを、後述のreactコンポーネント内の記述に合わせる

resources/views/stock.blade.php

@extends('layouts.app')

@section('content')
    @isset($stocks)
        <div id="stock" data-stocks='{{ $stocks }}'></div>
    @endisset
@endsection

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    // 割愛
    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    // 割愛
<head>
<body>
  // 割愛
  <main class="py-4">
    @yield('content') // @sectionのcontentはここに展開される
  </main>
</body>

resources/js/components/Stock.js

import React, { useState, useEffect } from 'react'
import ReactDOM from 'react-dom'

const Stock = () => {    
  // 割愛
}

export default Stock

// bladeファイル内の <div id="stock">に対してこのコンポーネントがレンダリングされる
if (document.getElementById('stock')) {
    ReactDOM.render(<Stock />, document.getElementById('stock'))
}

laravelのbladeファイルから変数をreactコンポーネントに渡す方法

ポイントは以下です

  • controllerから変数を返却する際にjsonに変換する
  • bladeで変数を受け取り、data属性を使用してjs側へ送信する
    js側へ送信する方法はほかにもいくつかあるようです。(専用のライブラリを使用する方法等)
  • js側で受け取ったjsデータをパースする(そこからはmapでイテレートする等自由です。)

app/Http/Controllers/StockController.php

// 割愛

class StockController extends Controller
{
    public function index(StockFetchService $stockFetchService)
    {
        $stocks = json_encode(
            array_map(
                // 割愛
            )
        );
        return view('stock', compact('stocks'));
    }
}

resources/views/stock.blade.php

bladeの{{}}内に変数を記述すると、PHPのhtmlspecialchars関数によってエスケープ処理してくれますが、htmlspecialcharsは半角スペースは変換してくれないので注意

@extends('layouts.app')

@section('content')
    @isset($stocks)
        <div id="stock" data-stocks='{{ $stocks }}'></div>
    @endisset
@endsection

resources/js/components/Stock.js

import React, { useState, useEffect } from 'react'
import ReactDOM from 'react-dom'

const Stock = () => {
    const element = document.getElementById('stock')
    var stockList = []

    if (element && element.dataset.stocks) {
        stockList = JSON.parse(element.dataset.stocks)
    }

  const [stocks, setStocks] = useState([])

    useEffect(() => {
        setStocks(stockList)
    }, [])

    return (
        <div className="container">
            <h5>在庫一覧</h5>
            {stocks.map((stock) => (
                <div>
                    <div className="d-flex"></div>
                    <table className="table">
                        <thead>
                            <tr>
                                <th scope="col">#</th>
                                <th scope="col">商品画像</th>
                                <th scope="col">商品名</th>
                                <th scope="col">在庫数</th>
                                <th scope="col">賞味期限</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <th scope="row">1</th>
                                <td>
                                    <img
                                        src={stock.imageUrl}
                                        alt={`${stock.name} の画像`}
                                    />
                                </td>
                                <td>
                                    <a
                                        key={stock.id.toString()}
                                        href={stock.url}
                                    >
                                        {stock.name}
                                    </a>
                                </td>
                                <td>{stock.number}</td>
                                <td>{stock.expiryDate}</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            ))}
        </div>
    )
}

export default Stock

if (document.getElementById('stock')) {
    ReactDOM.render(<Stock />, document.getElementById('stock'))
}

react側のformでcsrf_tokenを設置する方法

ポイントは以下です

  • app.blade.phpのheadタグ内でmetaタグを使用してcontent属性にトークンをセットします
  • reactコンポーネント内でcsrf_tokenを宣言し、それをformタグ内のinputタグで使用する

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    // 割愛
    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    // 割愛
<head>
<body>
  // 割愛
  <main class="py-4">
    @yield('content') // @sectionのcontentはここに展開される
  </main>
</body>

resources/js/components/Stock.js

const Stock = () => {

let csrf_token = document.head.querySelector(
'meta[name="csrf-token"]'
).content

// 割愛

return (
        <div className="container">
            <h5>在庫一覧</h5>

            {stocks.map((stock) => (
                <div>
                    <div className="d-flex"></div>
                    <table className="table">
                        <thead>
                            <tr>
                                <th scope="col">#</th>
                                <th scope="col">商品画像</th>
                                <th scope="col">商品名</th>
                                <th scope="col">在庫数</th>
                                <th scope="col">賞味期限</th>
                                <th scope="col"></th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <th scope="row">1</th>
                                <td>
                                    <img
                                        src={stock.imageUrl}
                                        alt={`${stock.name} の画像`}
                                    />
                                </td>
                                <td>
                                    <a
                                        key={stock.id.toString()}
                                        href={stock.url}
                                    >
                                        {stock.name}
                                    </a>
                                </td>
                                <td>{stock.number}</td>
                                <td>{stock.expiryDate}</td>
                                <td>
                                    <form
                                        method="GET"
                                        action={`"/stocks/edit/"${stock.id}`}
                                    >
                                        <input
                                            type="hidden"
                                            name="id"
                                            value={stock.id}
                                        />
                                        <input type="submit" value="編集" />
                                    </form>
                                </td>
                                <td>
                                    <form
                                        method="POST"
                                        action={`"/stocks/delete/"${stock.id}`}
                                    >
                                        <input
                                            type="hidden"
                                            name="_token"
                                            value={csrf_token}
                                        />
                                        <input
                                            type="hidden"
                                            name="id"
                                            value={stock.id}
                                        />
                                        <input type="submit" value="削除" />
                                    </form>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            ))}
        </div>
    )
}

以上です。

まとめ

いかがでしたでしょうか。本記事では、Laravelで部分的にReactコンポーネントを使用する際のTipsについて紹介しています。サーバー側からフロントエンド側に変数を渡したりする方法などが初見ではわかりにくいと感じたので、同じような構成を検討している方は参考にしてみていただけると幸いです。