How to ... in Next.js -01

How to ... in Next.js -01

server component / client component

在 Rendering 的策略上,Next.js 13 提供了一個新的概念,server componentclient component 這兩個可以讓我們決定哪些 component 是要在 server 上 render 還是在 client。

實際做法是在檔案的最上方新增 "use client""use server" directives 來做區分。

Next.js 預設情況下都是 server component。

'use client'; // directives

import { useState } from 'react';

export default function LikeButton() {
  const [likes, setLikes] = useState(0);

  function handleClick() {
    setLikes(likes + 1);
  }

  return <button onClick={handleClick}>Like ({likes})</button>;
}

<Link> component

<Link> 是 Next.js 對原生 <a> tag 的功能擴充,額外提供 prefetching 及 client-side navigation 的功能。

prefetch

  • 預設是 true,當 <Link> 在使用者的 viewport 中被看見都會先 prefetch,意思就是頁面會在 background 先被載入 。

Routes

Dynamic Routes

建立動態路由的寫法就是在 segment 外面加上 [ ] ,例如:[id][slug]

而之後r會傳入 layoutpageparams 裡面。

export default function Page({ params }) {
  return <div>My Post: {params.slug}</div>
}
RouteExample URLparams
app/blog/[slug]/page.js/blog/a{ slug: 'a' }
app/blog/[slug]/page.js/blog/b{ slug: 'b' }
app/blog/[slug]/page.js/blog/c{ slug: 'c' }

Catch-all Segments

可以捕捉到指定路由之下的所有動態路由 segment,寫法是 [...folderName]

RouteExample URLparams
app/shop/[...slug]/page.js/shop/a{ slug: ['a'] }
app/shop/[...slug]/page.js/shop/a/b{ slug: ['a', 'b'] }
app/shop/[...slug]/page.js/shop/a/b/c{ slug: ['a', 'b', 'c'] }

Route Groups

通常 folder structure 會對應到 URL Path,但如果你想要將 router 進行分類而又不影響 URL Path 的話,可以在 folder 外面加上 (),例如 (folderName)

此外,在 router group 底下建立 layout.js 還可以達到 different nested layout 的需求。

可以看到 /account/cart 會共享 (shop) layout 與外部的 app layout。而 /checkout 只會有 app layout。

Fetching Data on the Server with fetch

  • Next.js 13 將原生 fetch API 做功能上的擴充,例如 Caching Data(資料不用每次都去請求一次)、Revalidating Data(設定何時重新抓取資料,以確保資料的最新狀態)

  • 可以在 server component 使用 fetch API 搭配 async / await

  • Request Memoization:React 會在 component tree 之間,記憶相同的 fetch request 。

圖片來源:https://nextjs.org/docs/app/building-your-application/caching#request-memoization

async function getData() {
  const res = await fetch('https://api.example.com/...')

  if (!res.ok) {

    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}

generateStaticParams

此方法可以讓 dynamic routes 結合 static rendering,也就是讓這些 dynamic routes 是在 build time 就被建立,而不是 request time。

dynamicParams

Route Segment Config 可以設定 dynamic segment 沒有出現在 generateStaticParams 的時候,在請求階段自動產生頁面,預設是 true,如果是 false 就會回傳 404。

export const dynamicParams = true // true | false

notFound

  • 呼叫 notFound function 會去該 segment 內 render not-found.js

Loading UI & Streaming

  • 在同一個資料夾內建立一個 loading.js,Next.js 會自動將 page.js 包在 <Suspense> boundary。

  • 畫面一開始載入會先顯示 loading.js ,等 rendering 完畢之後再顯示 page.js

圖片來源:https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#instant-loading-states

export default function Loading() {
  // You can add any UI inside Loading, including a Skeleton.
  return <LoadingSkeleton />
}

streaming:Loading UI 不只可以用在整個 page.js,還可以套用在 child component。

圖片來源:https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense

import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>Loading feed...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>Loading weather...</p>}>
        <Weather />
      </Suspense>
    </section>
  )
}