Next.js: ナビゲーション: Link とプログラム API

最終更新:2026-08-26

ナビゲーションは地下鉄システムのようなものです。自動化された路線 (Link コンポーネント)、手動制御のオプション (プログラムナビゲーション)、高速通勤のための急行 (ソフトナビゲーション)、乗り換えルート (ハードナビゲーション) があります。

1. 学ぶこと



2. ジュニア開発者の実話

(1) ペインポイント: ページ間のナビゲーション体験が悪い

Charlie は最近 Next.js で e コマースサイトを開発していますが、ナビゲーション体験が悪いことに気づきました:

「ユーザーがホームページから商品リンクをクリックすると、ページが読み込まれるまで1秒間真っ白になります。ユーザーが『戻る』をクリックするたびに、ページ全体がリロードされます。さらにバグもあります。フォームを半分入力した状態で誤って商品リンクをクリックし、『戻る』をクリックするとフォームデータがすべて消えます。」

具体的な問題:

問題 影響 ユーザーフィードバック
ページ切り替え時の空白画面 ぎこちないナビゲーション 「リンクをクリックしてもなかなか表示されない」
フォームデータの消失 ユーザーがデータを再入力 「半分書いたのに戻ったら消えてた」
プリロードが賢くない よく訪問するページをプリロードしない 「ホームから商品詳細への遷移が一番遅い」
スクロール位置のリセット リストページでページング後に先頭に戻る 「毎回下までスクロールし直さないといけない」

(2) Next.js ナビゲーションの解決策

Link コンポーネントの自動フェッチ + ソフトナビゲーションの状態保持 + scroll={false} でスクロール位置を保持。

TSX
// 最適化された商品一覧ページのナビゲーション
<Link
  href={`/products/${product.id}`}
  prefetch={true}
  scroll={false}
  className="block p-4 border rounded hover:shadow"
>
  {product.title}
</Link>

(3) 効果

次元 最適化前 (標準 <a> タグ) 最適化後 (<Link> コンポーネント)
ページ読み込み速度 1〜2秒 (空白画面) 即時 (プリフェッチキャッシュ)
フォームデータの永続性 ページアンロード時に消失 ソフトナビで保持
スクロール位置の保持 先頭に戻る 正確に保持
ユーザー体験評価 3.2/5 4.8/5


(1) 基本的な使い方

100%
graph LR
    A[Link コンポーネント] --> B[クライアントサイドナビゲーション<br/>フルページリフレッシュなし]
    A --> C[自動プリフェッチ<br/>ビューポート内のリンク]
    A --> D[スクロール制御<br/>scroll={false}]
    A --> E[履歴の置換<br/>replace]

    style A fill:#cce5ff
    style B fill:#d4edda
💻 出力:

TEXT 📖 参照専用
Diagram of client-side navigation using Link component.
TSX
// ============================================
// Link コンポーネントの基本的な使い方
// ============================================

import Link from "next/link";

export default function Navigation() {
  return (
    <nav className="flex gap-6 p-4 bg-white shadow-sm">
      {/* 基本ナビゲーション */}
      <Link href="/" className="text-blue-600 hover:underline">
        Home
      </Link>

      {/* 動的ルーティング */}
      <Link href="/products/42" className="text-blue-600 hover:underline">
        Product 42
      </Link>

      {/* クエリパラメータ付き */}
      <Link
        href="/products?category=electronics&sort=price"
        className="text-blue-600 hover:underline"
      >
        Electronics
      </Link>

      {/* 完全な URL */}
      <Link
        href="https://help.example.com"
        className="text-blue-600 hover:underline"
      >
        Help Center
      </Link>
    </nav>
  );
}
💻 出力:

TEXT 📖 参照専用
Renders: Navigation links using Next.js Link component for client-side routing.
💻 出力:

TEXT 📖 参照専用
ブラウザに4つの青いリンクが表示:
Home → / 
Product 42 → /products/42
Electronics → /products?category=electronics&sort=price
Help Center → https://help.example.com (外部リンク)

(2) prefetch

デフォルトでは、Link コンポーネントはビューポート内のリンクをプリロードします。プリロード動作はサーバーとクライアントで異なります:

環境 prefetch={true} prefetch={false}
サーバーサイドレンダリング ページデータと RSC ペイロードをプリフェッチ プリフェッチしない
静的ページ ページ全体をプリロード プリロードしない
ビューポート内 デフォルト動作 プリロードしない

▶ サンプル: prefetch 動作の制御

💻 出力:

TEXT 📖 参照専用
The page renders as described above, with the UI updating based on the described behavior.
TSX
// ============================================
// Link のプリフェッチ動作を制御
// ============================================

import Link from "next/link";

export default function ProductList() {
  const products = [
    { id: 1, name: "Wireless Mouse" },
    { id: 2, name: "Keyboard" },
    { id: 3, name: "Monitor" },
  ];

  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold mb-4">Products</h1>

      {/* デフォルト prefetch: ビューポート内のリンクを自動プリロード */}
      {products.map(p => (
        <Link
          key={p.id}
          href={`/products/${p.id}`}
          className="block p-4 border-b hover:bg-gray-50"
        >
          {p.name}
        </Link>
      ))}

      {/* プリフェッチをオフ: あまり使わないページ向け */}
      <Link
        href="/archive"
        prefetch={false}
        className="block mt-4 text-gray-500"
      >
        View Archive (older products)
      </Link>

      {/* 強制プリフェッチ: 次に訪問するページ向け */}
      <Link
        href="/checkout"
        prefetch={true}
        className="block mt-4 px-6 py-2 bg-blue-600 text-white text-center rounded"
      >
        Proceed to Checkout
      </Link>
    </div>
  );
}
💻 出力:

TEXT 📖 参照専用
Maps over products to render list.
Content: Products
💻 出力:

TEXT 📖 参照専用
ページが読み込まれた後、ネットワークコンソールで観察:
1. /products/1、/products/2、/products/3 がプリフェッチされる (ビューポート内)
2. /archive はプリフェッチされない (prefetch={false})
3. /checkout がプリフェッチされる (prefetch={true}、ビューポート外でも)

ユーザーがリンクをクリックした場合:
- プリフェッチ済みページ → 即時表示 (キャッシュから読み取り)
- プリフェッチされていないページ → ネットワークリクエスト後に表示

(3) スクロール制御

100%
graph LR
    A[ナビゲーショントリガー] --> B{scroll プロパティ}
    B -->|scroll=true デフォルト| C[新しいページの先頭にスクロール]
    B -->|scroll=false| D[現在のスクロール位置を保持]

    style C fill:#f8d7da
    style D fill:#d4edda

▶ サンプル: scroll={false} でスクロール位置を維持

💻 出力:

TEXT 📖 参照専用
Diagram: Navigation Trigger; Scroll to the top of the new page; Keep the current scroll position.
TSX
// ============================================
// 商品一覧 + モーダルウィンドウ: scroll=false で一覧位置を維持
// ユーザーが商品をクリックして詳細を表示、戻ったときに一覧がスクロールしない
// ============================================

import Link from "next/link";

export default function ProductGrid() {
  const products = Array.from({ length: 20 }, (_, i) => ({
    id: i + 1,
    name: `Product ${i + 1}`,
  }));

  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold mb-6">Product Catalog</h1>
      <div className="grid grid-cols-4 gap-4">
        {products.map(p => (
          <Link
            key={p.id}
            href={`/products/${p.id}`}
            scroll={false}
            className="border p-4 rounded hover:shadow-lg"
          >
            <div className="h-32 bg-gray-100 rounded" />
            <p className="mt-2 font-medium">{p.name}</p>
          </Link>
        ))}
      </div>
    </div>
  );
}
💻 出力:

TEXT 📖 参照専用
Maps over products to render list.
Content: Product Catalog
💻 出力:

TEXT 📖 参照専用
1. ユーザーが3ページ目までスクロール (2000px 下方にスクロール)
2. 15番目の商品をクリックして商品詳細ページへ
3. ブラウザの戻るボタンをクリック
4. 一覧ページに戻り、スクロール位置が 2000px に保持される (「先頭に戻る」なし)
5. 比較: scroll={false} なしの場合、毎回先頭に戻る

(4) replace: 履歴の置換

操作 push (デフォルト) replace
ブラウザ履歴 新しいエントリを追加 現在のエントリを置換
戻るボタンの動作 前のページに戻る 置換されたページに移動
フォーム送信後 フォームページに戻れない 戻れる (フォームページをスキップ)

▶ サンプル: フォームシナリオでの replace 使用

💻 出力:

TEXT 📖 参照専用
The component renders the described UI in the browser.
TSX
// ============================================
// replace で履歴を置換: 送信後にフォームページに戻れないようにする
// ============================================

import Link from "next/link";

export default function CheckoutPage() {
  return (
    <div className="max-w-md mx-auto p-8">
      <h1 className="text-2xl font-bold mb-6">Checkout</h1>

      <div className="space-y-4">
        <input placeholder="Card number" className="w-full p-3 border rounded" />
        <input placeholder="Expiry date" className="w-full p-3 border rounded" />
        <input placeholder="CVV" className="w-full p-3 border rounded" />
      </div>

      {/* replace を使用: 送信後、このページに戻れなくなる */}
      <Link
        href="/order-confirmation"
        replace
        className="block mt-6 w-full p-3 bg-blue-600 text-white text-center rounded"
      >
        Place Order
      </Link>

      <p className="text-sm text-gray-500 mt-2 text-center">
        After placing order, back button will skip this page
      </p>
    </div>
  );
}
💻 出力:

TEXT 📖 参照専用
Content: Checkout | Place Order | After placing order, back button wi
💻 出力:

TEXT 📖 参照専用
ユーザーフロー:
1. ホーム → ショッピングカート → チェックアウト → 確認ページ
2. チェックアウトページで「Place Order」をタップ
3. ブラウザのアドレスが /order-confirmation に変わる
4. ユーザーが戻るボタンをクリック
5. チェックアウトページをスキップして、ショッピングカートページに直接移動
6. ユーザーが誤ってチェックアウトページに戻って再度注文するのを防止


4. useRouter によるプログラムナビゲーション

(1) API クイックリファレンス

100%
graph TB
    A[useRouter] --> B[push(url) - 新しいページにナビゲート]
    A --> C[replace(url) - 現在の履歴を置換]
    A --> D[back() - 戻る]
    A --> E[forward() - 進む]
    A --> F[refresh() - このページを更新]
    A --> G[prefetch(url) - プログラム的プリフェッチ]

    style A fill:#cce5ff
メソッド パラメータ 説明 ブラウザ動作
push href: string 新しい URL にナビゲート 履歴に追加
replace href: string 現在の履歴を置換 履歴を置換
back なし ブラウザの戻る history.back() と同じ
forward なし ブラウザの進む history.forward() と同じ
refresh なし このページを更新 サーバーサイドレンダリング (RSC)
prefetch href: string ページをプリフェッチ RSC ペイロードをキャッシュ

▶ サンプル: プログラムナビゲーション

💻 出力:

TEXT 📖 参照専用
Diagram of client-side navigation using Link component.
TSX
// ============================================
// useRouter プログラムナビゲーションの完全な例
// ============================================

'use client';

import { useRouter } from "next/navigation";

export default function NavigationBar({ userId }: { userId: string }) {
  const router = useRouter();

  return (
    <div className="p-4 bg-white shadow-sm">
      <div className="flex gap-4 max-w-4xl mx-auto">
        {/* push: ホームページに移動 */}
        <button
          onClick={() => router.push("/")}
          className="px-4 py-2 bg-blue-600 text-white rounded"
        >
          Home
        </button>

        {/* push: 動的ルーティング */}
        <button
          onClick={() => router.push(`/users/${userId}`)}
          className="px-4 py-2 bg-gray-600 text-white rounded"
        >
          Profile
        </button>

        {/* replace: 現在のページを置換 */}
        <button
          onClick={() => router.replace("/login")}
          className="px-4 py-2 bg-red-600 text-white rounded"
        >
          Logout
        </button>

        {/* back/forward: 閲覧履歴 */}
        <button
          onClick={() => router.back()}
          className="px-4 py-2 border rounded"
        >
          ← Back
        </button>
        <button
          onClick={() => router.forward()}
          className="px-4 py-2 border rounded"
        >
          Forward →
        </button>

        {/* refresh: このページを更新 (サーバーサイド再レンダリング) */}
        <button
          onClick={() => router.refresh()}
          className="px-4 py-2 border rounded"
        >
          Refresh ↻
        </button>
      </div>
    </div>
  );
}
💻 出力:

TEXT 📖 参照専用
NavigationBar renders its UI.
💻 出力:

TEXT 📖 参照専用
各ボタンをクリック:
[Home]    → / にナビゲート
[Profile] → /users/123 にナビゲート
[Logout]  → 現在の履歴を /login で置換 (戻れない)
[← Back]  → ブラウザの戻るボタン
[Forward] → ブラウザの進むボタン
[Refresh] → このページを更新 (RSC 再レンダリング、ページ全体のリロードは発生しない)

▶ サンプル: フォーム送信後のナビゲーション

💻 出力:

TEXT 📖 参照専用
The page renders as described above, with the UI updating based on the described behavior.
TSX
// ============================================
// フォーム送信: 検証 → 保存 → プログラムナビゲーション
// ============================================

'use client';

import { useRouter } from "next/navigation";
import { useState } from "react";

export default function CreateProjectForm() {
  const router = useRouter();
  const [saving, setSaving] = useState(false);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);

    try {
      // API 呼び出しをシミュレート
      await new Promise(resolve => setTimeout(resolve, 1500));
      // 成功後、新しいプロジェクトページにナビゲート
      router.push("/projects/42");
      // データキャッシュをリフレッシュ
      router.refresh();
    } catch (err) {
      console.error("Failed to create project:", err);
      alert("Failed to create project. Please try again.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="max-w-lg mx-auto p-8 space-y-4">
      <h1 className="text-2xl font-bold">Create Project</h1>
      <input
        name="name"
        placeholder="Project name"
        className="w-full p-3 border rounded"
        required
      />
      <textarea
        name="description"
        placeholder="Description"
        className="w-full p-3 border rounded h-32"
      />
      <button
        type="submit"
        disabled={saving}
        className="w-full p-3 bg-blue-600 text-white rounded disabled:opacity-50"
      >
        {saving ? "Creating..." : "Create Project"}
      </button>
    </form>
  );
}
💻 出力:

TEXT 📖 参照専用
Form with: name.
Content: Create Project
💻 出力:

TEXT 📖 参照専用
1. ユーザーがプロジェクト名と説明を入力
2. "Create Project" ボタンをクリック
3. ボタンが "Creating..." に変わる (無効化)
4. 1.5秒後 (シミュレートされた API 遅延)
5. ページが /projects/42 にナビゲート
6. プロジェクト一覧データがリフレッシュされる (新規作成されたプロジェクトを含む)


5. redirect() と <Redirect>

(1) 2種類のリダイレクト

100%
graph TB
    A[リダイレクトリクエスト] --> B[サーバーサイド<br/>redirect()]
    A --> C[クライアントサイド<br/><Redirect>]
    B --> D[サーバーアクション / ルートハンドラ]
    B --> E[サーバーコンポーネント]
    C --> F[クライアントコンポーネント]
    C --> G[条件付きレンダリング時]

    style B fill:#d4edda
    style C fill:#cce5ff
メソッド 使用場所 トリガータイミング パフォーマンス
redirect() サーバーアクション / サーバーコンポーネント サーバーレスポンス時 サーバーリダイレクト、クライアントオーバーヘッドゼロ
<Redirect> クライアントコンポーネント レンダリング時 クライアントサイドリダイレクト、ルート変更


6. ソフトナビゲーション vs ハードナビゲーション

(1) 比較

100%
graph TB
    subgraph "ソフトナビゲーション (Link/useRouter)"
        A[クライアントサイドルーティング] --> B[ページナビゲーション<br/>フルページリフレッシュなし]
        B --> C[レイアウトはマウント維持]
        B --> D[React 状態保持]
        B --> E[RSC ペイロード置換]
    end

    subgraph "ハードナビゲーション (ブラウザ全体のリフレッシュ)"
        F[ブラウザのフルページローディング] --> G[ページナビゲーション<br/>ページ全体をリフレッシュ]
        G --> H[レイアウト再マウント]
        G --> I[全状態リセット]
        G --> J[全 JS/CSS ローディング]
    end

    style A fill:#d4edda
    style F fill:#f8d7da
特性 ソフトナビゲーション ハードナビゲーション
トリガー方法 <Link>useRouter() ブラウザリフレッシュ、<a> タグ、window.location
ページ全体のリフレッシュ ❌ いいえ ✅ はい
レイアウト保持 ✅ 保持される ❌ 再マウント
RSC ペイロード 増分更新 フルダウンロード
パフォーマンス 瞬時 500ms〜2s
状態維持


7. ナビゲーションローディングインジケータ

(1) 実装計画

100%
graph TB
    A[ナビゲーショントリガー] --> B[ローディングインジケータ]
    B --> C[トッププログレスバー<br/>NProgress スタイル]
    B --> D[Suspense フォールバック<br/>スケルトン表示]
    B --> E[loading.tsx<br/>ページレベルローディング]

    style A fill:#cce5ff
    style B fill:#f8d7da


8. 完全な例: e コマースナビゲーションシステム

TSX
// ============================================
// 総合例: e コマースの包括的なナビゲーションシステム
// Link、useRouter、redirect、ナビゲーションローディング状態をカバー
// ============================================

// src/app/layout.tsx — グローバルナビゲーションバー
import Link from "next/link";
import { CartCount } from "./CartCount";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <header className="bg-white shadow-sm sticky top-0 z-40">
          <div className="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
            <Link href="/" className="text-2xl font-bold text-blue-600">
              ShopHub
            </Link>

            <nav className="hidden md:flex gap-6">
              <Link href="/products" className="hover:text-blue-600">
                Products
              </Link>
              <Link href="/categories" className="hover:text-blue-600">
                Categories
              </Link>
              <Link href="/deals" prefetch={false} className="hover:text-blue-600">
                Deals
              </Link>
              <Link href="/about" className="hover:text-blue-600">
                About
              </Link>
            </nav>

            <div className="flex items-center gap-4">
              <Link href="/search" className="text-gray-600 hover:text-blue-600">
                Search
              </Link>
              <Link href="/cart" className="relative text-gray-600 hover:text-blue-600">
                Cart
                <CartCount />
              </Link>
              <Link
                href="/account"
                className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm"
              >
                Sign In
              </Link>
            </div>
          </div>
        </header>

        <main className="max-w-6xl mx-auto px-4 py-8">
          {children}
        </main>
      </body>
    </html>
  );
}

// src/app/products/[id]/page.tsx — 商品詳細 (ナビゲーションロジックを含む)
'use client';
import { useRouter } from "next/navigation";
import Link from "next/link";

export default function ProductDetail({ params }) {
  const router = useRouter();

  async function handleAddToCart() {
    await fetch("/api/cart", {
      method: "POST",
      body: JSON.stringify({ productId: params.id }),
    });
    // カートに追加後、カート内の数量をリフレッシュ
    router.refresh();
  }

  async function handleBuyNow() {
    await fetch("/api/cart", {
      method: "POST",
      body: JSON.stringify({ productId: params.id, quantity: 1 }),
    });
    // 今すぐ購入 → チェックアウトに進む (replace で商品ページに戻れないようにする)
    router.replace("/checkout");
  }

  return (
    <div>
      {/* パンくずナビゲーション */}
      <nav className="text-sm text-gray-500 mb-6">
        <Link href="/" className="hover:text-blue-600">Home</Link>
        <span className="mx-2">/</span>
        <Link href="/products" className="hover:text-blue-600">Products</Link>
        <span className="mx-2">/</span>
        <span className="text-gray-900">{params.id}</span>
      </nav>

      <div className="flex gap-8">
        <div className="w-1/2">
          <img
            src={`https://picsum.photos/seed/${params.id}/400/400`}
            alt="Product"
            className="w-full rounded-lg"
          />
        </div>
        <div className="w-1/2">
          <h1 className="text-3xl font-bold">Product #{params.id}</h1>
          <p className="text-2xl text-green-600 font-bold mt-4">$49.99</p>
          <p className="text-gray-600 mt-4">
            High-quality product with premium features.
          </p>

          <div className="flex gap-4 mt-8">
            <button
              onClick={handleAddToCart}
              className="flex-1 px-6 py-3 border-2 border-blue-600 text-blue-600 rounded-lg hover:bg-blue-50"
            >
              Add to Cart
            </button>
            <button
              onClick={handleBuyNow}
              className="flex-1 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
            >
              Buy Now
            </button>
          </div>

          <div className="mt-8 border-t pt-6">
            <Link
              href={`/products/${Number(params.id) - 1}`}
              scroll={false}
              className="text-blue-600 hover:underline"
            >
              ← Previous Product
            </Link>
            <Link
              href={`/products/${Number(params.id) + 1}`}
              scroll={false}
              className="text-blue-600 hover:underline float-right"
            >
              Next Product →
            </Link>
          </div>
        </div>
      </div>
    </div>
  );
}

// src/app/CartCount.tsx — ショッピングカート数量表示
export async function CartCount() {
  const cart = await fetch("https://api.example.com/cart");
  const itemCount = cart?.items?.length ?? 0;

  if (itemCount === 0) return null;

  return (
    <span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs w-5 h-5 rounded-full flex items-center justify-center">
      {itemCount}
    </span>
  );
}

期待される出力:

TEXT 📖 参照専用
ナビゲーションバー (上部固定):
[ShopHub]  Products  Categories  Deals  About  [Search]  Cart(3)  [Sign In]

商品詳細ページ (/products/1):
Home / Products / 1
[商品画像]  [商品詳細]
            Product #1
            $49.99
            [Add to Cart] [Buy Now]
            ← Previous Product | Next Product →

ナビゲーション動作:
- Products をクリック → ソフトナビ、瞬時にページ切り替え
- Deals をクリック → 事前プリフェッチなし (prefetch=false)、ただしナビゲーションは依然高速
- カートに追加 → router.refresh()、カートアイコン更新
- 今すぐ購入 → router.replace("/checkout")、履歴を置換

❓ よくある質問

Q Link コンポーネントと &lt;a&gt; タグの違いは何ですか?
A Link コンポーネントはクライアントサイドでルーティングを処理し、ページ全体のリフレッシュをトリガーせず、レイアウトを保持し、プリロードをサポートします。&lt;a&gt; タグはページ全体のリロード (ハードナビゲーション) をトリガーし、すべての React 状態が失われます。Next.js はすべての内部ルーティングに Link を使用します。
Q useRouter を使用するコンポーネントに 'use client' を含める必要があるのはなぜですか?
A useRouter は React フックであり、クライアントコンポーネントでのみ使用できます。サーバーコンポーネントにはブラウザ API (historylocation など) へのアクセスがないため、ルーティング関連のフックはクライアントサイドで実行されます。
Q router.refresh()window.location.reload() の違いは何ですか?
A router.refresh() は Next.js のソフトリフレッシュで、サーバーサイドの RSC ペイロードを再リクエストし、変更された部分のみを更新しながらクライアントサイドの状態 (useStateContext など) を保持します。window.location.reload() はページ全体のリフレッシュを実行し、すべての状態が失われ、JS と CSS の完全な再読み込みが必要です。
Q Link の prefetch はモバイルデバイスでもトリガーされますか?
A はい。prefetch はビューポート内のリンクに対してトリガーされ、モバイルデバイスでも同様です。ただし、Next.js はユーザーのデータ使用量を考慮し、低速ネットワーク (2G/3G) では大きなページをプリフェッチしない場合があります。prefetch={false} で手動制御することもできます。
Q redirect()<Redirect> はいつ使用すべきですか?
A redirect() はサーバーサイド (サーバーアクション、サーバーコンポーネント、ルートハンドラ) で使用され、303/307 レスポンスを送信します。クライアントサイドの条件付きリダイレクトには、<Redirect> コンポーネントの代わりにアクセス制御ロジック (「アクセス拒否」メッセージ + ナビゲーションボタンの表示) を使用します。
Q ナビゲート中に URL は変わるのにページ内容が変わらない場合、どうすればいいですか?
A これは通常、同じ React key またはキャッシュを使用していることが原因です。次のことを試してください: 1) ページコンポーネントで異なる key を使用して強制再構築; 2) router.refresh() を呼び出してサーバーサイドデータをリフレッシュ; 3) データがレイアウトによってキャッシュされているかどうかを確認。

📖 まとめ


📝 練習問題

  1. 基礎問題 (⭐): ページに5つのリンクを作成し (Home, About, Contact, Product List, Product Details #42)、ブラウザのネットワークタブでどの URL がプリロードされるか観察してください。

  2. 発展問題 (⭐⭐): 「ログイン → ダッシュボード」ワークフローを実装してください。サーバーアクションを使用してログインを検証し、成功した場合は redirect() でダッシュボードにリダイレクトし、失敗した場合は redirect() でログインページに戻りエラーパラメータを含めてください。

  3. チャレンジ (⭐⭐⭐): 無限スクロールのリストページを実装してください。ユーザーが商品をクリックして詳細ページに移動する際、scroll={false} でスクロール位置を保持し、戻ったときにリストが以前の位置に留まるようにしてください。戻るナビゲーションには router.back() を使用してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%