Laravel+React+Inertiaでフラッシュメッセージを表示する

はじめに

Laravelでフラッシュメッセージを表示する方法は簡単です。ただ、フロントエンドをReactで実装する場合は手順が異なります。今回はInertiaを使って簡単に実装する方法をメモしました。

環境:

  • PHP: 8.2.x
  • Laravel: 10.x
  • React: 18.2.x
  • Inertia : 1.0.12

Laravel+Bladeで実装する場合

Laravelでは以下のように、コントローラからwithメソッドでセッションにフラッシュデータを保存し、Blade上でsessionメソッドを使って取得して表示するだけです。

// コントローラで渡す
return to_route('customers.index')
        ->with('message', '登録成功しました。');

// Bladeで表示する
@if (session('message'))
    <div className="alert alert-success">
        {{ session('message') }}
    </div>
@endif

Reactの場合は上記のようなsessionメソッドは使えないため、どのようにデータの取得するか考える必要があります。その手順を次から説明していきます。

Laravel+Inertia+Reactでの実装手順

Inertiaを使って、各リクエストで自動的にセッションデータをpropsに渡すように設定します。

この方法はInertiaドキュメントのShared dataという項目で説明されています。今回のフラッシュメッセージに限らず、複数ページで表示したい共通データを毎回手動でpropsに渡す手間を省くことができます。

Laravelからセッションデータを渡す

Bladeを使う場合と同じで、コントローラからwithメソッドでデータをセッションに保存します。

例えば、CustomerControllerで登録処理完了後にメッセージと共にindexページへリダイレクトさせるとします。

// CustomerController.php

public function store(CustomerStoreRequest $request): RedirectResponse
{
    Customer::create([
        // 登録処理
    ]);

    return to_route('customers.index')->with('message', '登録成功しました。');
}

セッションを共有データに設定

HandleInertiaRequestsミドルウェアのshareメソッドに、共有したいデータを連想配列の形で定義します。

// app/Http/Middleware/HandleInertiaRequests.php
class HandleInertiaRequests extends Middleware
{
    public function share(Request $request)
    {
        return array_merge(parent::share($request), [
            'flash' => [
                'message' => fn () => $request->session()->get('message')
            ],
        ]);
    }
}

ここでは、Laravelのセッションからmessageというキーでフラッシュメッセージを取得してflashキーに追加しています。フラッシュメッセージの種類が増えたらflash配列に追加していきます。

Reactで共有データを取得

InertiaのusePageフックを使うことで、ページに渡されたpropsを参照できます。今回flashというデータを取り出す場合、以下のように分割代入で取り出します。

// resources/js/Pages/Customer/Index.jsx

import { usePage } from "@inertiajs/react";

const { flash } = usePage().props;

もしくは、以下のようにも書くことができます。

const flash = usePage().props.flash;

あとはflash.messageで値にアクセスして、メッセージが存在する時だけ以下のように表示します。

{flash.message && (
  <div className="alert alert-success">{flash.message}</div>
)}

注意点

Shared dataを使用する際には以下の点に注意が必要です。

  • 他のpropsと名前が競合しないように注意すること
  • 全てのレスポンスに共有データが含まれるため、無駄に多くのデータを共有しないようにすること

参考ソース