Laravelのviewを、bladeからinertiaで部分的にReact化する



[個人でやってるサイト](https://www.dojin-music.info/)で、Reactの学習も兼ねて色々触ろうかと思うなかで、初めは別機能の部分を新規でNextJSで作ろうと思っていたのですが、一部機能だけとかだとログイン周りとかの処理が面倒で、そのままテンプレだけReactに移行できればと思って行いました。
というので、色々調べていたらLaravel->Reactはできそうというので、webpackを使うでもなく素bladeで作成されたプロジェクトの一部をReact化しました。

## 環境

Laravel 6だったところから、9にバージョンアップしたもの
なのでLaravelではViteがデフォルトになってますが、新規で作ってないのでvite.configとかもない状態でした

## Nodeとyarnを入れる

この辺はメインの話ではないので雑に。下記から手に入れます。node18.14で実行しています。

https://nodejs.org/ja/

ちなみに今回は関係ないですが、普段バージョン切り替えなどが必要なこともあるため私は `n` を利用しています。

https://github.com/tj/n

yarnはこちら

https://yarnpkg.com/getting-started/install

## Viteの導入

素のBladeと書いたので、Viteが動かない状態です。

https://readouble.com/laravel/9.x/ja/vite.html

各種パッケージを追加します。もしかしたらLaravel9ではもともとpackage.jsonに記載されているかもしれません。

```
yarn add -D vite laravel-vite-plugin @vitejs/plugin-react
```

設定ファイルはLaravel9から作っていれば初期からファイルがありそうですが、アップデートしたものなので自分で置きました。
またここで、 `@vitejs/plugin-react` を利用するため記載内容が少し異なります。

```javascript:vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from "@vitejs/plugin-react";

export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/js/app.jsx"],
            refresh: true,
        }),
        react()
    ]
});
```

下のpackage.jsonも同様で、必要に応じて追記・上書きします。
webpackやそれ以外ですでに、devやbuildを使っているのであれば、別の名前をつけましょう。

```json:package.json
  "scripts": {
    "dev": "vite",
    "build": "vite build",

  }
```


## Inertiaの導入

LaravelのデータをReactにわたす場合は、Inertiaを使うようです

https://inertiajs.com/

### PHP側（サーバー側）

https://inertiajs.com/server-side-setup

```
composer require inertiajs/inertia-laravel
```

Bladeは完全には消えず、 `app.blade.php` に下記のようなものを置く必要があるようです。

```html:resources/views/app.blade.php
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    @viteReactRefresh
    @vite('resources/js/app.jsx')
    @inertiaHead
  </head>
  <body>
    @inertia
  </body>
</html>
```

すでに `app.blade.php` を利用済みなら変更は可能で、上のsetupページと下記のリンクが参考になりそうです。
https://stackoverflow.com/questions/63926485/change-inertiajs-laravel-default-rootview

```
php artisan inertia:middleware
```

これで `Http/Middleware/HandleInertiaRequests.php` というミドルウェアのファイルが追加されます。多分コマンドを使うことでAppのnamespaceがちゃんと設定されるようです。
ちなみに、こいつを `git add` し忘れて本番で動かず困りましたｗ
このMiddlewareをKernelに追加

```php:app/Http/Kernel.php
'web' => [
    // ...
    \App\Http\Middleware\HandleInertiaRequests::class,
],
```

`\App` の部分は独自のnamespaceを利用している場合は適宜変える必要があります。

### JS側（クライアント側）

https://inertiajs.com/client-side-setup

```
yarn add @inertiajs/react
```

ドキュメントには記載する内容は書いてあるが、どこに置くかが書いてないですが、上で設定値として記載した `resource/js/app.jsx` に置きます。
ドキュメントでは `resource/js/app.js` って書いてありますが、`<App />` を使っているため、jsxである必要があります。

```javascript:resource/js/app.jsx
import { createInertiaApp } from '@inertiajs/react'
import { createRoot } from 'react-dom/client'

createInertiaApp({
  resolve: name => {
    // TypeScriptなら.tsx
    const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true })
    return pages[`./Pages/${name}.jsx`]
  },
  setup({ el, App, props }) {
    createRoot(el).render(<App {...props} />)
  },
})
```

実際にはTypeScriptにしたり、PostCSSとかSASSの導入、Storybookの導入などなどいろいろパッケージは必要だと思います。ここでは主題ではないので飛ばします。

### 参考：Jetsteram（かBreeze）

JetstreamでInertia等を導入することもできるようですが、新規のLaravelアプリケーションのみのようなので、今回はInertiaを直接入れています

https://jetstream.laravel.com/2.x/installation.html

## PHP LaravelのControllerの書き換え

既存のページのview呼び出しを書き換えます。まだ作っていないディレクトリ構造なので適当に。

```diff
- return view('dir.template', []);
+ return Inertia::render('Dir/Template', []);
```

## Reactの導入

Reactそのものは入れてない状態なので入れます

```
yarn add react react-dom
```

## JSX（TSX）の追加

上記で指定したパスにJSXなりTSXを置きます

```jsx:resources/js/Pages/Dir/Template.jsx
export default function Template () {
    return (
        <>
            <h1>タイトル</h1>
            <div>ほげほげ</div>
        </>
    )
}
```

## 値をテンプレートに渡す

Bladeと同様に、第二引数が渡されます。

```php
return Inertia::render('Dir/Template', ['title' => 'test']);
```

```jsx:resources/js/Pages/Dir/Template.jsx
export default function Template (props) {
    return (
        <>
            <h1>{props.title}</h1>
            <div>ほげほげ</div>
        </>
    )
}
```
TypeScript使ったほうが型を定められて便利かと思います

## 起動

```
yarn dev
```

## アクセス

これでPHPサーバーの該当パスにアクセスすると表示されるはずです。
（自分の場合はlocalhost）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140947/04ad1256-587d-3aec-a26d-781f3e598d45.png)

また、jsxのファイルを触るとブラウザ側に自動で反映されるはずです。めちゃ速い！
もちろん他のパスはこれまで通りBladeのVIEWに飛びます。

うまく出ないときは

- WEBブラウザのデベロッパーツールからconsoleエラー確認
- viteを立ち上げたコンソールの確認
- PHP側のエラーの確認
- サーバー側のエラーの確認

あたりの確認になると思います

## Controllerの単体テスト

Controllerのactionの返り値は変わるので当然単体テストも変える必要があります。詳細は下記リンクで。

https://inertiajs.com/testing

## TypeScript対応

この辺はViteの話になってしまうため、雑に記載します。

```
yarn create vite
```

とやると初期のプロジェクトが作られます。このときに `React` `TypeScript` を選べば
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140947/05e21fa6-c999-d9e9-d741-2bbe49585047.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140947/7a5dc231-b70a-4864-7456-ba2399bd5e44.png)
`tsconfig.json` 等必要なファイルが作られるので、それを参考にするのが良いと思います。

## おわりに

PHPのときは割とViewでHelper使ったり、ゴリゴリ処理入れたりしがちでしたが、JS（React）で処理する場合はロジックはちゃんとサーバー側に寄せて色々処理し、Viewでは表示に専念させるかたちにするのは大事だと感じました
という点で単にViewだけ切り替えてというのができない場合もありますが、部分的にReact化できるのは移行の面では非常に便利だと感じました。