こんにちは。株式会社リンクネット、ソリューション事業部の森正です。
最近弊社ではLaravel + Reactを使用した開発が増えています。
その際に Inertia.js
を利用することが多いのですが、 Inertia.js
を使うことで比較的簡単にSPAを実装することができます。
本記事では Inertia.js
の使い方について紹介します。
$ sail artisan -V
Laravel Framework 9.52.0
$ sail php -v
PHP 8.2.7 (cli) (built: Jun 8 2023 15:27:40) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.7, Copyright (c) Zend Technologies
with Zend OPcache v8.2.7, Copyright (c), by Zend Technologies
with Xdebug v3.2.1, Copyright (c) 2002-2023, by Derick Rethans
今回はLaravelのスターターキット「Laravel Breeze」を使用しInertiaとReactをインストールしていきます。
$ sail composer require laravel/breeze --dev
$ sail artisan breeze:install react
$ npm install
インストール後のパッケージ一覧
$ npm list --depth=0
/home/morimasa/projects/blog/react-inertia
├── @headlessui/react@1.7.15
├── @inertiajs/react@1.0.9
├── @tailwindcss/forms@0.5.3
├── @vitejs/plugin-react@3.1.0
├── autoprefixer@10.4.14
├── axios@1.4.0
├── laravel-vite-plugin@0.7.8
├── lodash@4.17.21
├── postcss@8.4.24
├── react@18.2.0
├── react-dom@18.2.0
├── tailwindcss@3.3.2
└── vite@4.3.9
画面となるコンポーネントを用意します。
今回は下記のSampleコンポーネントを用意しました。
import { Head } from '@inertiajs/react';
const Sample = (props) => {
return (
<>
<Head title="Sample" />
<div className='m-3'>
<p>Laravel + React + Inertiaを使用したサンプルです。</p>
</div>
</>
);
}
export default Sample;
Laravelでbladeを表示する際と同様にrouteを設定します。
controllerではbladeではなくInertiaを使用しコンポーネントを返してください。
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
use Inertia\Inertia; // Inertiaを読み込む
class Controller extends BaseController
{
/**
* サンプル
*
* @return \Inertia\Response
*/
public function sample()
{
return Inertia::render('Sample'); // 表示するコンポーネントを指定
}
}
http://localhost/sample にアクセスすると作成したコンポーネントが表示されます。
次に作成したコンポーネントに値を渡してみます。
先ほど作成したcontrollerを修正します。
第2引数に渡したい変数を指定することで、propsに値を渡すことができます。
// ... 省略
public function sample()
{
$text = 'controllerから渡されました。';
return Inertia::render('Sample', [
'text' => $text
]);
}
コンポーネントで受け取り、画面に表示してみます。
// ... 省略
const Sample = (props) => {
const { text } = props;
return (
<>
<Head title="Sample" />
<div className='m-3'>
<p>Laravel + React + Inertiaを使用したサンプルです。</p>
<p>{ text }</p>
</div>
</>
);
}
controllerから渡された情報が表示されました。
実際に製造を行っていると、複数のアクションから共通の値を渡したい場面も出てくると思います。
そういった場合は下記のように __construct
を使用し各アクションから呼び出していました。
// ... 省略
class Controller extends BaseController
{
private $common_text;
public function __construct()
{
$this->common_text = '共通のテキストです。';
}
/**
* サンプル
*
* @return \Inertia\Response
*/
public function sample()
{
$text = 'controllerから渡されました。';
return Inertia::render('Sample', [
'text' => $text,
'commonText' => $this->common_text
]);
}
/**
* サンプル2
*
* @return \Inertia\Response
*/
public function sample2()
{
return Inertia::render('Sample2', [
'commonText' => $this->common_text
]);
}
}
これでも良いのですが、inertiaで用意されている Middleware/HandleInertiaRequests
を使用することで、簡単に実装することができます。
参考URL:https://inertiajs.com/shared-data
HandleInertiaRequests
にpropsを追加
class HandleInertiaRequests extends Middleware
{
// ... 省略
/**
* Define the props that are shared by default.
*
* @return array<string, mixed>
*/
public function share(Request $request): array
{
return array_merge(parent::share($request), [
'auth' => [
'user' => $request->user(),
],
'ziggy' => function () use ($request) {
return array_merge((new Ziggy)->toArray(), [
'location' => $request->url(),
]);
},
'commonText' => '共通のテキストです。' // 追加
]);
}
}
Controller
から __construct
を削除
// ... 省略
class Controller extends BaseController
{
/**
* サンプル
*
* @return \Inertia\Response
*/
public function sample()
{
$text = 'controllerから渡されました。';
return Inertia::render('Sample', [
'text' => $text
]);
}
/**
* サンプル2
*
* @return \Inertia\Response
*/
public function sample2()
{
return Inertia::render('Sample2');
}
}
どちらも共通propsを受け取ることができました。
このように HandleInertiaRequests
を使用することで Controller
から毎回共通propsを渡す必要がなくなります。
実際はリクエスト時にデータベースを更新するような動きになると思いますが、
今回はサンプルとして、リクエストがあった場合にpropsを上書きする処理を作りました。
class Controller extends BaseController
{
// ... 省略
/**
* サンプル3
*
* @return \Inertia\Response
*/
public function sample3(Request $request)
{
$params = $request->all();
$name = '山田 太郎';
$address = '石川県 金沢市';
if (!empty($params)) {
// リクエストがあった場合、$nameと$addressを上書き
$name = $params['name'];
$address = $params['address'];
}
return Inertia::render('Sample3', compact(
'name',
'address'
));
}
}
リクエストを送信するには
router.post(第1引数:URL, 第2引数:パラメータ, 第3引数:オプション);
で実装できます。
※第1引数でLaravelのrouteを指定する場合は ziggy.js
が必要になるのでインストールしておきましょう。
npm i ziggy-js
下記の onSubmit
がリクエスト送信処理のサンプルになります。
import React, { useState } from 'react';
import { Head, router } from '@inertiajs/react';
import route from 'ziggy-js';
const Sample3 = (props) => {
const { name, address } = props;
const [values, setValues] = useState({ name: '', address: '' });
// stateの更新
const changeValue = (event) => {
setValues((values) => ({ ...values, [event.target.name]: event.target.value }))
}
// リクエスト送信
const onSubmit = () => {
router.post(route('sample3'), values, { preserveState: false });
}
return (
<>
<Head title="Sample3" />
<div className='m-3'>
<p>リクエスト送信サンプル</p>
</div>
<div className='m-3'>
<div>■氏名</div>
<div className='mr-3'>{ name }</div>
<input type="text" name="name" value={ values.name } onChange={ changeValue } />
</div>
<div className='m-3'>
<div>■住所</div>
<div className='mr-3'>{ address }</div>
<input type="text" name="address" value={ values.address } onChange={ changeValue } />
</div>
<div className='m-3'>
<button type="button" onClick={ onSubmit }>更新</button>
</div>
</>
);
}
export default Sample3;
実際に試してみます。
変更したい内容を入力
「更新」押下
氏名、住所ともに入力した内容に更新されました。
第三引数には様々なオプションを指定できるのですが、今回はよく使うオプションをいくつか紹介したいと思います。
true
を指定することで状態を保持ことができます。
上の例で true
を指定すると、「更新」押下後も入力した内容が保持されるようになります。
指定したpropsのみ更新することができます。
上の例で[only: 'name']を指定すると、「更新」押下後、氏名のみが更新されるようになります。
リクエストが開始されたタイミングで実行されます。
読み込み中のインジケーター等を表示する際に便利です。
リクエストが終了したタイミングで実行されます。
読み込み中のインジケーター等を非表示にする際に便利です。
※リクエストの成功、失敗に関わらず実行されるので注意してください。
リクエストが成功した場合に実行されます。
成功後に状態を更新したい場合等に便利です。
リクエストが失敗した場合に実行されます。
バリデーションエラーを受け取る際に便利です。
※その他のオプション・イベントについては、下記に記載されています。
https://inertiajs.com/manual-visits
今回は Inertia.js
について紹介しました。
Inertia.js
を使用することで、普段Laravelを使用している時と同じ感覚でSPAを作ることができ、とても便利ですが、
フロントエンドとバックエンドの開発者が異なる場合には向いていないため、開発方法に応じて axios
等と使い分けることが大切です。