見出し画像

inertia.jsのpartial reloadを(今んとこ)世界一詳しくコード付きで解説するドキュメント

誰得なんだよこれ…


ここにある説明じゃ全然わからんから手を動かしてみようという企画

準備

まず、ここでは会社一覧と、ユーザー一覧を出すページを想定する。

完成予想図

つまりuserscompaniesのためのtableを用意する。これらは非常に適当でいいが、数が必要である。

Companyの準備

とりあえずcompaniesテーブルの構造

        Schema::create('companies', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });

まあこんな感じだろう。で、seedする
database/seeders/DatabaseSeeder.php 

\App\Models\Company::factory(10)->create();

などとしておき、factoryを作る
database/factories/CompanyFactory.php 

    public function definition(): array
    {
        return [
            'name' => fake()->company,
        ];
    }

このfactoryを元に10個seedしているので以下のような10個ダミー会社が出来るはずである(fakerのlocaleは日本語であるとする、場合によっては英語になってるかもしれんがここでは解説しない)

> Company::get(['id', 'name'])
= Illuminate\Database\Eloquent\Collection {#7336
    all: [
      App\Models\Company {#7328
        id: 1,
        name: "有限会社 渡辺",
      },
      App\Models\Company {#7327
        id: 2,
        name: "有限会社 小泉",
      },
      App\Models\Company {#7324
        id: 3,
        name: "有限会社 田中",
      },
      App\Models\Company {#7310
        id: 4,
        name: "株式会社 大垣",
      },
      App\Models\Company {#7322
        id: 5,
        name: "有限会社 井上",
      },
      App\Models\Company {#7321
        id: 6,
        name: "有限会社 佐々木",
      },
      App\Models\Company {#7343
        id: 7,
        name: "有限会社 村山",
      },
      App\Models\Company {#7344
        id: 8,
        name: "有限会社 大垣",
      },
      App\Models\Company {#7342
        id: 9,
        name: "有限会社 石田",
      },
      App\Models\Company {#7358
        id: 10,
        name: "有限会社 木村",
      },
    ],
  }

userの準備

そしたらユーザーのファクトリーも適当にcompanyから割り当てるようにしよう。ここではcompany_idというカラムを供えるものとする

        return [
            'company_id' => Company::inRandomOrder()->first()->id,
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];

前準備はこれでok。

ユーザーを一覧するjsx

まあここでは本格的にユーザー管理をするわけじゃないがとりあえず

artisan make:controller UserController -m User -r

このようにUserController の作成が必要だろう。routes/web.phpには適当にresourceを当てておく

Route::resource('users', UserController::class);

ここまでで/users/までのルートが出来らので、一覧を書いていく 
app/Http/Controllers/UserController.php 

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

use App\Models\User;
use App\Models\Company;
use Illuminate\Http\Request;

use Inertia\Inertia;
use Inertia\Response;

class UserController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): Response
    {
        $users = User::latest()->get();
        $companies = Company::latest()->get();
        return Inertia::render('Users/Index', [
            'users'     => $users,
            'companies' => $companies,
        ]);
    }

そしたらInertia::renderで指定されているように、viewを書く。こんな感じだろう
resources/js/Pages/Users/Index.jsx 

import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head } from '@inertiajs/react';

export default function UserIndex({ auth, users, companies }) {
  return (
    <AuthenticatedLayout
      user={auth.user}
      header={
        <h2 className="font-semibold text-xl text-gray-800 leading-tight">
          Users
        </h2>
      }
    >
      <Head title="Users" />

      <div className="py-12">
        <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
          <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
            {/* ユーザーテーブルの追加 */}
            <table className="min-w-full divide-y divide-gray-200">
              <thead className="bg-gray-50">
                <tr>
                  <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    ID
                  </th>
                  <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Email
                  </th>
                  <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Name
                  </th>
                </tr>
              </thead>
              <tbody className="bg-white divide-y divide-gray-200">
                {users.map((user) => (
                  <tr key={user.id}>
                    <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.id}</td>
                    <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.email}</td>
                    <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.name}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </AuthenticatedLayout>
  );
}


ユーザー一覧が表示された

まあここはfakerで適当に作られているから、内容は適当なものとなるが、10人程度出ていることが重要である。

会社情報も出す

適当にササーっとrelationを組む

use Illuminate\Database\Eloquent\Relations\BelongsTo;

class User extends Authenticatable
{
    use SoftDeletes, HasApiTokens, HasFactory, Notifiable;

    public function company(): BelongsTo
    {
        return $this->belongsTo(Company::class);
    }

controllerもinertiaの場合いきなりオブジェクトを注入ってわけにはいかないので事前ロードする。ここではwithを使う。

    public function index(): Response
    {
        $users = User::with('company')->latest()->get();
        $companies = Company::latest()->get();
        return Inertia::render('Users/Index', [
            'users'     => $users,
            'companies' => $companies,
        ]);
    }

でまあこれに応じて適当にCompany列を増やす

                  <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Company
                  </th>
...
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.company.name}</td>
ユーザーがどの会社に所属しているか出てくるようになった

ここまでが前準備、といいたいところだがCompanyの一覧をまだ活用できていない。ここではPluckする

        $users = User::with('company')->latest()->get();
        $companies = Company::pluck('name', 'id');
        return Inertia::render('Users/Index', [
            'users'     => $users,
            'companies' => $companies,
        ]);

そしたら

            <div className="m-4 flex items-center">
              <select className="form-select appearance-none block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding bg-no-repeat border border-solid border-gray-300 rounded transition ease-in-out focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none">
                {Object.entries(companies).map(([id, name]) => (
                  <option key={id} value={id}>{name}</option>
                ))}
              </select>

              {/* フィルタリング用のボタン */}
              <button className="ml-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded focus:outline-none focus:shadow-outline">
                Filter
              </button>
            </div>

こんなのを付けておく

会社でフィルターできるっぽいものが出来た

ここまでで準備完了

filterボタンを押すととりあえずreloadするようにする

filterなのにreloadはしないw、つか、ボタンのclassがなげえからbreezeのPrimaryButtonを使おうか…

import PrimaryButton   from '@/Components/PrimaryButton'
...
              <PrimaryButton onClick={handleFilterClick}>
                Filter
              </PrimaryButton>

そうしたら、handleFilterClick 関数を書く

window.location.reloadの場合

export default function UserIndex({ auth, users, companies }) {
  const handleFilterClick = () => {
    window.location.reload();
  };

たとえば、こういうwindow.location.reloadの場合、ブラウザのF5を押したような挙動になる。というかこれが通常の文字通りのリロードというやつだろう。

inertiaのrouter.reload

が、これだとinertiaっぽくないので、inertiaのrouterをもってくる。これに関しては以下のドキュメントも合わせて参照して欲しい

とよいが、まあここではさくっと書いておく

import { router } from '@inertiajs/react'
export default function UserIndex({ auth, users, companies }) {
  const handleFilterClick = () => {
    router.reload();
  };

この状態だとページの動きがほとんど無いのがわかるだろう。

ChatGPTによる解説

しかし、これだと面白くないから、もうちょっとcontrollerに手を入れる

    public function index(): Response
    {
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));

        $companies = Company::pluck('name', 'id');
        return Inertia::render('Users/Index', [
            'users'     => $users,
            'companies' => $companies,
        ]);
    }

コードはどういう風でもいいけど、このようにusersに対してランダム性のある配列にしたのがポイントだ。

そうすると…ボタンを押すたびにランダムなユーザー列が取得されてくる。これもまたページのリフレッシュが発生していないことが理解可能である、はず。

うまいこと表示されるのかしら…

Partial Reloadの考え

前段が非常に長くなったが、ここからがPartial Reload。

さて、この際に実は「Company」のモデルには何も影響をおよぼしていない、そういう事はまあまあある。usersの配列の内容だけ変化するというような時だ、このようなとき「usersだけ変化させればいいんじゃないの?」というのがあるが、実際には当然ボタンを押すと両方にクエリが発行されている。これをまず確認してみよう

現状のクエリの確認

    public function index(): Response
    {
        // クエリログを有効にする
        \DB::enableQueryLog();

        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));

        $companies = Company::pluck('name', 'id');

        // クエリログを取得
        $queryLog = \DB::getQueryLog();

        // クエリログをLaravelのログに記録
        \Log::info('Query Log:', $queryLog);

        return Inertia::render('Users/Index', [
            'users'     => $users,
            'companies' => $companies,
        ]);
    }

こんな風にしておくとlaravel.logにはこんな風に出てくるだろう、ボタンを押すたびに

[2023-12-19 03:14:57] local.INFO: Query Log: [{"query":"select * from `users` where `users`.`deleted_at` is null order by `created_at` desc","bindings":[],"time":0.54},{"query":"select * from `companies` where `companies`.`id` in (1)","bindings":[],"time":0.48},{"query":"select `name`, `id` from `companies`","bindings":[],"time":0.38}]
[2023-12-19 03:15:37] local.INFO: Query Log: [{"query":"select * from `users` where `users`.`deleted_at` is null order by `created_at` desc","bindings":[],"time":0.6},{"query":"select * from `companies` where `companies`.`id` in (1)","bindings":[],"time":0.45},{"query":"select `name`, `id` from `companies`","bindings":[],"time":0.42}]

これは

  • select * from `users`

  • select * from `companies`

とかでわかるようにusersとcompaniesに対してクエリーが発行されている(ただし、もちろんusersに関連したcompanyを取得するクエリーもあるので実は3つあるが)


Partial Reloadしてみる

このドキュメントのスニペットに以下のようなものがある

import { router } from '@inertiajs/react'

router.reload({ only: ['users'] })

これをやってみよう

export default function UserIndex({ auth, users, companies }) {
  const handleFilterClick = () => {
    // window.location.reload();
    // router.reload();
    router.reload({ only: ['users'] })
  };

これは単純にreloadする際にusersだけを求めるという機能になる。ただ、現在のコントローラーのコードを改めて見てみると

        // クエリログを有効にする
        \DB::enableQueryLog();

        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));

        $companies = Company::pluck('name', 'id');

        // クエリログを取得
        $queryLog = \DB::getQueryLog();

        // クエリログをLaravelのログに記録
        \Log::info('Query Log:', $queryLog);

        return Inertia::render('Users/Index', [
            'users'     => $users,
            'companies' => $companies,
        ]);

これだと何も発生しない。こういった場合はPartialReloadのリクエストかどうかを判別する必要がある(ドキュメントにはあまり書いてないけど)

Partial Reloadリクエストかどうかの判別

    public function index(Request $request): Response
    {
        dd($request->header('X-Inertia-Partial-Data'));

このような感じでX-Inertia-Partial-Data を参照してみると…

このようにPartial reloadの場合は必要なpropsが表示されいる。これは

ここではusersが表示されている

ということは

    public function index(Request $request): Response
    {
        $partialData = $request->header('X-Inertia-Partial-Data');

        // クエリログを有効にする
        \DB::enableQueryLog();
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));

        if ($partialData) {
            // クエリログを取得
            $queryLog = \DB::getQueryLog();
            // クエリログをLaravelのログに記録
            \Log::info('Partial query Log:', $queryLog);
            return Inertia::render('Users/Index', [
                'users' => $users,
            ]);
        }  else {
            $companies = Company::pluck('name', 'id');
            // クエリログを取得
            $queryLog = \DB::getQueryLog();
            // クエリログをLaravelのログに記録
            \Log::info('Query Log:', $queryLog);
            return Inertia::render('Users/Index', [
                'users'     => $users,
                'companies' => $companies,
            ]);
        }
    }

このようにpartialDataがあるときは

            return Inertia::render('Users/Index', [
                'users' => $users,
            ]);

このようにcompaniesを取得せずusersだけ返却する、これで動作する

動作する、が、

この程度のためにこんなコードを複雑にする必要は通常ありえないだろう。

で、クエリーログをコントローラーのコードに紛れこませると面倒くせえことになるのでリスナーを使う

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        DB::listen(function ($query) {
            // クエリ、バインディング、実行時間をログに記録します
            Log::info($query->sql, ['bindings' => $query->bindings, 'time' => $query->time]);
        });
    }

これは実験的なので本番では使わないように。そうすればコントローラーがちょっとすっりいする

    public function index(Request $request): Response
    {
        $partialData = $request->header('X-Inertia-Partial-Data');
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));

        if ($partialData) {
            return Inertia::render('Users/Index', [
                'users' => $users,
            ]);
        }  else {
            $companies = Company::pluck('name', 'id');
            return Inertia::render('Users/Index', [
                'users'     => $users,
                'companies' => $companies,
            ]);
        }
    }

さて、この状態で条件分岐が非常にうっとうしいだろう。これを無くしてしまうとするとどうか

    public function index(Request $request): Response
    {
        $partialData = $request->header('X-Inertia-Partial-Data');
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));

        $data = [
            'users'     => $users,
            'companies' => $partialData ? null : Company::pluck('name', 'id'),
        ];

        return Inertia::render('Users/Index', $data);
    }

partialDataがあるときは、companies、nullを返却するようになった。

さて、ここで初回のクエリーを見てみよう。

[2023-12-20 08:14:20] local.INFO: select * from `users` where `id` = ? and `users`.`deleted_at` is null limit 1 {"bindings":[11],"time":2.28}
[2023-12-20 08:14:20] local.INFO: select * from `users` where `users`.`deleted_at` is null order by `created_at` desc {"bindings":[],"time":0.68}
[2023-12-20 08:14:20] local.INFO: select * from `companies` where `companies`.`id` in (1) {"bindings":[],"time":0.5}
[2023-12-20 08:14:20] local.INFO: select `name`, `id` from `companies` {"bindings":[],"time":0.58}

続いてpartial reloadしたときのクエリをみてみる

[2023-12-20 08:15:12] local.INFO: select * from `users` where `id` = ? and `users`.`deleted_at` is null limit 1 {"bindings":[11],"time":2.63}
[2023-12-20 08:15:12] local.INFO: select * from `users` where `users`.`deleted_at` is null order by `created_at` desc {"bindings":[],"time":0.64}
[2023-12-20 08:15:12] local.INFO: select * from `companies` where `companies`.`id` in (1) {"bindings":[],"time":0.52}

ここでの違いで重要なのは

 select `name`, `id` from `companies`

このクエリは2回目のpartial reloadでは含まれていないことだ。まあ三項演算子で省いているので当然といえば当然であるが

さらに改造する

    public function index(): Response
    {
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));
        $data = [
            'users'     => $users,
            'companies' => fn () => Company::pluck('name', 'id'),
        ];

        return Inertia::render('Users/Index', $data);
    }

このようにヘッダーを見るのをやめ

        $data = [
            'users'     => $users,
            'companies' => fn () => Company::pluck('name', 'id'),
        ];

ここでcompaniesに対して遅延データー評価を発動することでcompaniesは初回のみロードされることになる。

てか、なかなか難しいよねこの概念。手を動かさないとわからないかもしれないね。

partial reloadを使ったvisit

たとえば、今indexはusersとcompaniesを律儀に返却しているが、usersだけ返却するコントローラーを作ってもいい。ほぼフルコピーでpartialという名前のメソッドを作ろう。

    public function partial(Request $request): Response
    {
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));
        return Inertia::render('Users/Index', ['users' => $users]);
    }


これはもうcompaniesを渡してすらおらずusers=>$usersしか発動していない。
そしたらこんな風にrouteを立てて

    Route::get('users/partial', [UserController::class, 'partial'])->name('users.partial');
    Route::resource('users', UserController::class);

ここに向かってvisitしてみると

  const handleFilterClick = () => {
    // window.location.reload();
    // router.reload();
    // router.reload({ only: ['users'] })
    router.visit(route('users.partial'), { only: ['users'] })
  };

これは正しく動作する、が、問題がある。この場合/users/partialに文字通りvisitしてしまうのでurlが/users/partialになっている。この状態でブラウザーのリロードをすると当然

    public function partial(Request $request): Response
    {
        // 全ユーザーを取得し、その数を確認
        $allUsers = User::with('company')->latest()->get();
        $userCount = $allUsers->count();

        // ランダムな数(1〜ユーザー総数)のユーザーを取得
        $users = $allUsers->random(rand(1, $userCount));
        return Inertia::render('Users/Index', ['users' => $users]);
    }

このコードが評価される、つまり、companiesが無いのでエラーとなるわけだ。このように、partial reloadを使う場合はそのinertiaの特性と戦いながら使い方をよく考えないといけない

つまりapiエンドポイントのように使うとわりとコケる

reloadとvisitでの重要な違い

stateを用意してみよう。一度reloadとかvisitとかは削除しておく

import React, { useState } from 'react';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head } from '@inertiajs/react';
import { router } from '@inertiajs/react'
import PrimaryButton   from '@/Components/PrimaryButton'

export default function UserIndex({ auth, users, companies }) {

  const [isFiltered, setIsFiltered] = useState(false);

  const handleFilterClick = () => {
    setIsFiltered(!isFiltered); // 状態を反転させる
    // window.location.reload();
    // router.reload();
    //  router.reload({ only: ['users'] })
    // router.visit(route('users.partial'), { only: ['users'] })
  };

これに基いてボタンを少し変更する

              <PrimaryButton onClick={handleFilterClick} className="mr-2">
                Filter
              </PrimaryButton>
              {isFiltered ? (
                <span className="inline-block bg-blue-500 text-white text-xs px-2 rounded-full uppercase font-semibold tracking-wide">
                  1
                </span>
              ) : (
                <span className="inline-block bg-gray-300 text-gray-700 text-xs px-2 rounded-full uppercase font-semibold tracking-wide">
                  2
                </span>
              )}

つまり、このよう1だの2だのがstateにより変化するはずだ

1と2がtoggleしているだけ

まあこれはpartial reloadまったく関係なくreactの機能でしかないんだけど、これを使う場合注意必要である

router.reloadを使う場合

  const handleFilterClick = () => {
    setIsFiltered(!isFiltered); // 状態を反転させる
    // window.location.reload();
    // router.reload();
    router.reload({ only: ['users'] })
    // router.visit(route('users.partial'), { only: ['users'] })
  };

これはrouter.reloadを使った例で(onlyはあってもなくてもいいけど)とにかく、この状態だとstateは完全に維持される。stateのボタンがtoggleしながらtableの内容が変化しているのが理解できるだろう

visitの場合

おなじ条件でちょっとコードを変化させてみよう

  const handleFilterClick = () => {
    setIsFiltered(!isFiltered); // 状態を反転させる
    // window.location.reload();
    // router.reload();
    // router.reload({ only: ['users'] })
    router.visit(route('users.partial'), { only: ['users'] })
  };

わかりますか?visitはそれを行うごとにstateがリセットされる。だからstateをキープしたまま一部のページリロードを行いたい場合はvisitは使えない

最後にchatgptを用いてドキュメントの訳っぽいものを載せていこう

ここまで踏まえた上で最後公式のわずかなドキュメントを翻訳し、解説を付けていく

同じページを再訪する際に、そのページの全データをサーバーから再取得する必要は常にあるわけではありません。実際、ページの一部のデータのみを選択することは、ページデータの一部が古くなるのを許容できる場合に、パフォーマンス最適化の役立つ手段となることがあります。Inertiaは「パーシャルリロード」という機能を通じてこれを可能にします。

例として、「ユーザーインデックス」ページを考えてみましょう。このページにはユーザーのリストと、会社別にユーザーをフィルタリングするオプションが含まれています。ページに最初にアクセスする際、ユーザーと会社の両方のプロップがページコンポーネントに渡されます。しかし、同じページに再度アクセスする際(例えば、ユーザーをフィルタリングするために)、サーバーからユーザーデータのみをリクエストし、会社のデータはリクエストしなくてもよくなります。Inertiaはその後、サーバーから返された部分的なデータを、クライアント側のメモリ内に既に存在するデータと自動的にマージします。
※ パーシャルリロードは、同じページコンポーネントへの訪問にのみ適用されます。

https://inertiajs.com/partial-reloads

訳注:  まあこれは実際に今まで長々と解説してきたような事を言っている。当然同じコンポーネントでしか使えないのだが、解説してこなかった所だったので一部太字にした

Partial visitの実行
パーシャルリロードを実行するには、only プロパティを使用してサーバーが返すべきデータを指定します。このオプションは、プロップスのキーに対応するキーの配列であるべきです

https://inertiajs.com/partial-reloads
import { router } from '@inertiajs/react'

router.visit(url, {
  only: ['users'],
})

パーシャルリロードは、ユーザーが既にいる同じページコンポーネントにのみ行えるため、ほとんどの場合は現在のURLを自動的に使用する router.reload() メソッドを使用するのが理にかなっています

https://inertiajs.com/partial-reloads

訳注:  最初にvisitを紹介している割には二段目ではreloadの方がいいぞと言っているのが面白いんだけどstateとかの関係もあり、どう考えてもreloadで設計する方が正しい、となるとここには書いてないがbackendは大抵同一コントローラー同一メソッドで処理する事になるだろう

Inertiaリンクを使用して、only プロパティを使ってパーシャルリロードを行うことも可能です。

https://inertiajs.com/partial-reloads
import { Link } from '@inertiajs/react'

<Link href="/users?active=true" only={['users']}>Show active</Link>

訳注: 使い所は難しそうな気もするが、たとえばpagerとかのfilterだろうか

遅延データ評価

パーシャルリロードを最も効果的にするためには、サーバーサイドのルートやコントローラーからプロップを返す際に遅延データ評価を使用することも重要です。これは、すべてのオプショナルなページデータをクロージャに包むことで達成できます

Inertiaがリクエストを実行するとき、必要なデータを判断し、その時点で初めてクロージャを評価します。これにより、多くのオプショナルなデータを含むページのパフォーマンスが大幅に向上する可能性があります。

return Inertia::render('Users/Index', [
    // 初回訪問時に常に含まれる...
    // パーシャルリロード時にオプションで含まれる...
    // 常に評価される...
    'users' => User::get(),

    // 初回訪問時に常に含まれる...
    // パーシャルリロード時にオプションで含まれる...
    // 必要時にのみ評価される...
    'users' => fn () => User::get(),

    // 初回訪問時には決して含まれない...
    // パーシャルリロード時にオプションで含まれる...
    // 必要時にのみ評価される...
    'users' => Inertia::lazy(fn () => User::get()),
]);

訳注: ここでは2番目の例を利用した。いずれにせよ、partial reloadを使う場合はrenderの第二引数が重要になるからそこをちゃんと意識しておく。

まとめ

全体的にドキュメントも少なくスニペットもほとんどweb上にないので「現状世界一」としている。ただ、この例だとイマイチ使い所も微妙でありパフォーマンスとか言われてもそんなもんcompanyごとき全部取得したらいいんじゃないの?と思ってしまうね。次回はこの機能を使ったapiっぽい振舞いを考えてみよう。inertia.jsの場合はapiコールっぽいことをし辛いので、どうしてもこの機能をベースに設計していくことになりがちである。


この記事が気に入ったらサポートをしてみませんか?