💭

【ソース有】inertia.jsのLoad When Visibleのデモ

2024/12/23に公開

的な

オフィシャルドキュメントのリンク

https://inertiajs.com/load-when-visible

前準備

routes/web.php

Route::get('/load-when-visible', LoadWhenVisibleController::class)->name('load-when-visible');

app/Http/Controllers/LoadWhenVisibleController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;

class LoadWhenVisibleController extends Controller
{
    public function __invoke(Request $request): Response
    {
        $text = fake()->realText(2000);
        return Inertia::render('LoadWhenVisible', [
            'text' => $text,
        ]);
    }
}

resources/js/Pages/LoadWhenVisible.jsx

export default function LoadWhenVisible({ text }) {
  return (
    <div className="min-h-screen bg-gray-50">
      <header className="bg-white shadow">
        <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
          <h1 className="text-3xl font-bold text-gray-900">Load When Visible Demo</h1>
        </div>
      </header>

      <main className="py-10">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-12">
          {[...Array(3)].map((_, index) => (
            <section
              key={index}
              className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
            >
              <p className="text-gray-700 leading-relaxed">{text}</p>
            </section>
          ))}
        </div>
      </main>
    </div>
  );
}

とすると

こんな風になる。このダミーテキストの山を下に送ると何か出るみたいなデモを作る。

ユーザーのリストを追加

ここでさらにメソッドを追加し

app/Http/Controllers/LoadWhenVisibleController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;

class LoadWhenVisibleController extends Controller
{
    public function __invoke(Request $request): Response
    {
        $text = fake()->realText(2000);
        return Inertia::render('LoadWhenVisible', [
            'text'  => $text,
            'users' => $this->getUsers(),
        ]);
    }

    public function getUsers(): Collection
    {
        return User::all();
    }
}

と、ユーザー情報を全部取るみたいなよくあるgetUsers()的なデモを与えた

ここで初期のseedではユーザーが少ないので追加しておく

database/seeders/DatabaseSeeder.php

        User::factory()->create([
            'name' => 'Test User',
            'email' => 'test@example.com',
        ]);
        User::factory(100)->create();

そしたらview

resources/js/Pages/LoadWhenVisible.jsx

export default function LoadWhenVisible({ text, users }) {
  return (
    <div className="min-h-screen bg-gray-50">
      <header className="bg-white shadow">
        <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
          <h1 className="text-3xl font-bold text-gray-900">Load When Visible Demo</h1>
        </div>
      </header>

      <main className="py-10">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-12">
          {[...Array(3)].map((_, index) => (
            <section
              key={index}
              className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
            >
              <p className="text-gray-700 leading-relaxed">{text}</p>
            </section>
          ))}

          <section className="mt-8 p-6 bg-white rounded-lg shadow-md">
            <h2 className="text-2xl font-semibold text-gray-800 mb-4">User List</h2>
            <ul className="divide-y divide-gray-200">
              {users.map((user) => (
                <li key={user.id} className="py-4 flex items-center">
                  <div className="w-10 h-10 rounded-full bg-blue-500 flex-shrink-0 flex items-center justify-center text-white font-bold">
                    {user.name.charAt(0).toUpperCase()}
                  </div>
                  <div className="ml-4">
                    <p className="text-sm font-medium text-gray-900">{user.name}</p>
                    <p className="text-sm text-gray-500">ID: {user.id}</p>
                  </div>
                </li>
              ))}
            </ul>
          </section>
        </div>
      </main>
    </div>
  );
}

とすると

となる

いよいよWhenVisibleを加えていく

--- a/resources/js/Pages/LoadWhenVisible.jsx
+++ b/resources/js/Pages/LoadWhenVisible.jsx
@@ -1,3 +1,5 @@
+import { WhenVisible } from '@inertiajs/react'
+
 export default function LoadWhenVisible({ text, users }) {
   return (
     <div className="min-h-screen bg-gray-50">

とし、

<WhenVisible data={['users']} fallback={<div>Loading...</div>}>
  <div>moge</div>
</WhenVisible>

を置く。とりあえずユーザーのリストではなくmogeってみる

この時ロードに時間がかかってるテイを演出するために

    public function getUsers(): Collection
    {
        sleep(2);
        return User::all();
    }

sleep(2)を書き加えとる。しかし、これを行うと当然のようにこの関数がコールされて、ページ全体がおっそくなるので

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;

class LoadWhenVisibleController extends Controller
{
    public function __invoke(Request $request): Response
    {
        $text = fake()->realText(2000);
        return Inertia::render('LoadWhenVisible', [
            'text'  => $text,
            'users' => Inertia::optional(fn() => $this->getUsers()),
        ]);
    }

    public function getUsers(): Collection
    {
        sleep(5);
        return User::all();
    }
}

このようにInertia::optionalとする。

        return Inertia::render('LoadWhenVisible', [
            'text'  => $text,
            // 'users' => Inertia::optional(fn() => $this->getUsers()),
            'users' => fn () => $this->getUsers(),
        ]);

でもok。では実行してみよう

このように2秒後にmogeになったら、あとはもう勝ったようなもんやな

仕上げ

せっかくのreactなのでreact-spinnerも使ったろ

import { WhenVisible } from '@inertiajs/react'
import { ClipLoader } from 'react-spinners';

export default function LoadWhenVisible({ text, users }) {
  return (
    <div className="min-h-screen bg-gray-50">
      <header className="bg-white shadow">
        <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
          <h1 className="text-3xl font-bold text-gray-900">Load When Visible Demo</h1>
        </div>
      </header>

      <main className="py-10">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-12">
          {[...Array(3)].map((_, index) => (
            <section
              key={index}
              className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
            >
              <p className="text-gray-700 leading-relaxed">{text}</p>
            </section>
          ))}


          <section className="mt-8 p-6 bg-white rounded-lg shadow-md">
            <h2 className="text-2xl font-semibold text-gray-800 mb-4">User List</h2>
            <WhenVisible
              data={['users']}
              fallback={
                <div className="flex justify-center items-center py-6">
                  <ClipLoader size={35} color={"#2563eb"} />
                </div>
              }
            >
              <ul className="divide-y divide-gray-200">
                 {users && users.map((user) => (
                   <li key={user.id} className="py-4 flex items-center">
                     <div className="w-10 h-10 rounded-full bg-blue-500 flex-shrink-0 flex items-center justify-center text-white font-bold">
                     {user.name.charAt(0).toUpperCase()}
                     </div>
                     <div className="ml-4">
                       <p className="text-sm font-medium text-gray-900">{user.name}</p>
                       <p className="text-sm text-gray-500">ID: {user.id}</p>
                     </div>
                   </li>
                 ))}
              </ul>
            </WhenVisible>
          </section>
        </div>
      </main>
    </div>
  );
}

ソース

https://github.com/catatsumuri/inertiajs-infinite-scroll-react/releases/tag/load-when-visible

Discussion