inertia.jsで時間のかかるpostを処理する

とりあえず、inertiaが動いてる環境なら何でもいいけど今回はbreezeを使う事にする。環境は本当になんでもいいけどリソースフルなrouteを1つ用意しておくこと。っていうかまあ以下に書いてはいく
事前準備
リソースコントローラーの作成
artisan make:model Test -r
INFO Controller [app/Http/Controllers/TestController.php] created successfully.
リソースフルなTestControllerに
Route::resource('tests', TestController::class);
リソースルートを割り当て
app/Http/Controllers/TestController.php
<?php
namespace App\Http\Controllers;
use App\Models\Test;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
class TestController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): Response
{
return Inertia::render('Tests/Index');
}
Tests/IndexのInertiaビューを返す。
ってことはviewとなるのは resources/js/Pages/Tests/Index.jsx
import { useEffect } from 'react';
import { Head, Link, useForm } from '@inertiajs/react';
import GuestLayout from '@/Layouts/GuestLayout';
import PrimaryButton from '@/Components/PrimaryButton';
export default function TestIndex() {
const {
data, setData, post, processing, errors, reset,
} = useForm({ });
const submit = (e) => {
e.preventDefault();
post(route('tests.store'));
};
return (
<GuestLayout>
<Head title="Test" />
<form onSubmit={submit}>
<PrimaryButton className="ml-4" disabled={processing}>
Send
</PrimaryButton>
</form>
</GuestLayout>
);
}
ここで重要なのはuseFormであるが、まあここではこれの詳細を見るのはとりあえず置いておく
時間のかかるsend
今この状態でSendボタンを押すと
const submit = (e) => {
e.preventDefault();
post(route('tests.store'));
};
このコードによりtests.storeに転送される。このpostメソッドこそがinertia.jsが供えるものである。以下のuseFormの持つ機能をまとめてもらったものを貼っておく

、まあともあれ。Sendボタンを押すと、tests.storeによりstoreメソッドに転送される。このことは
public function store(Request $request)
{
dd("sent");
}
などで確認可能である。
なお、ここで
use Illuminate\Http\RedirectResponse; // 冒頭でuse
public function store(Request $request): RedirectResponse
{
return redirect(route('tests.index'));
}
このように書けば、redirectしたように見えるはずだ(実際には通常のhttpリクエストとは異なる)
時間のかかる処理をシミュレート
public function store(Request $request): RedirectResponse
{
sleep(10);
return redirect(route('tests.index'));
}
まあ10秒待てと
inertia.jsのprocessing
ここで
<PrimaryButton className="ml-4" disabled={processing}>
Send
</PrimaryButton>
なんとなく書いてきたこの disabled={processing}> これは何なのかというと、これはinertia.jsの機能ではある。これはsubmitが完了するまで0で、完了したら1になる仕組みであり、非同期処理で有効である。この辺はchatgptに解説してもらおう

まあともかく、この変数を眺めていれば処理が完了するまで別の表示を行う事が可能となるわけだ。なお、同期的な処理でもボタンを押したら即座にボタン凍結というような事は可能であった。ここではもう一歩踏み込んでみよう
Loading… をテキスト表示する
<PrimaryButton className="ml-4" disabled={processing}>
{processing ? (
<>
<span className="ml-2">Loading...</span>
</>
) : "Send"}
</PrimaryButton>
このような状態を取って表示できるのが非同期処理の良い所ではある。
これを実行してみよう

react-spinners
ここにあるような装飾のアイコンを即座に利用可能である。
npm install react-spinners
でインストールする。
あとはちょいちょい加えていくだけ
import { BounceLoader } from 'react-spinners';
でimportしたら
<PrimaryButton className="ml-4" disabled={processing}>
{processing ? (
<>
<BounceLoader size={20} color={"#ffffff"} />
<span className="ml-2">Loading...</span>
</>
) : "Send"}
</PrimaryButton>
こんな感じだ。これで冒頭の

これが実現可能