iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0

繼續介紹一些除了 page 以外 App Router 會識別的特殊文件名稱。

loading

現在 Next.js 預設 page 都是 Server Component,而當 Server Component 中在做資料請求或處理導致沒有即時送回元件的話,就能用 loading 暫時顯示一個載入狀態。

例如一個叫 console 的頁面結構如下。

console
├── loading.tsx
└── page.tsx
// page.tsx
export default async function Console() {
  await new Promise(resolve => setTimeout(resolve, 3000));

  return (
    <div>
      <h1>Welcome to Console!</h1>
    </div>
  );
}

page 中夾了一個測試用的載入 Promise ,讓元件延遲送回。

// loading.tsx
import { CircularProgress } from '@mui/material';

export default async function Loading() {
  return <CircularProgress />;
}

這樣在元件送回來前就會先顯示一個載入圈圈。

loading 裡實作的是 React 的 Suspense 功能。

error

如果頁面在請求資料時拋出了錯誤且沒被處理的話,就會使用 error 的元件來取代。

console
├── error.tsx
├── loading.tsx
└── page.tsx
// page.tsx
export default async function Console() {

  await new Promise((resolve, reject) =>
    setTimeout(() => reject(new Error('Console Error!')), 3000),
  );

  return (
    <div>
      <h1>Welcome to Console!</h1>
    </div>
  );
}

給剛剛的範例做點修改,改成載入結束後拋出錯誤。

// error.tsx
'use client';

import { useEffect, useState } from 'react';

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  const [message, setMessage] = useState(error.message);

  useEffect(() => {
    setMessage(error.message);
  }, [error]);

  return (
    <div>
      <h2>{message}</h2>
    </div>
  );
}

error 元件會被傳入錯誤資訊的 prop ,可以從中提取資料。

error 實作的是 React 的 ErrorBoundary 功能。

另 error 只能用 Client Component,不能是 Server Component。

又 error 產生的 ErrorBoundary 只會包著 page 元件,然後 layout 包在外面,所以 layout 的錯誤並不回被處理。但 layout 發生的錯誤會被更上層的 error 元件捕捉。

<Layout>
  <ErrorBoundary fallback={<Error/>}>
    <Page/>
  </ErrorBoundary>
</Layout>

因為 error 不能處理 layout 的錯誤,那最上層的 layout 假如發生了錯誤,該怎麼辦呢?

這裡就得定義一個特殊的 error handler。

app
├── global-error.tsx
├── layout.tsx
└── page.tsx

global-error 是最外圍的防線,基本上構造跟一般 error 相同,只是 global-error 為了能夠取代 layout,必須要有 html 跟 body 的標籤。

'use client';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <html lang="en">
      <body id="__next">
        <h2>Something went wrong!</h2>
        <button onClick={() => reset()}>Try again</button>
      </body>
    </html>
  );
}

not-found

當沒有對應的路由時的替換頁面,放在 app 底下的話就是取代預設的 404 頁面。

app
├── page.tsx 
└── not-found.tsx

動態路由中也能用,只是只有當動態路由的頁面使用 notFound 函式時才會導向 not-found 頁面。

user
└── [id]
    ├── not-found.tsx
    └── page.tsx
// page.tsx
import { notFound } from 'next/navigation';

export interface IdProps {
  params: {
    id: string;
  };
}

export default function User({ params: { id } }: IdProps) {
  if (id !== '1') {
    notFound();
  }

  return (
    <div>
      <h1>Welcome User {id}!</h1>
    </div>
  );
}

上一篇
Next App Router 其一
下一篇
Next App Router 其三
系列文
組裝前端開發工具箱,從 NX 入手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言