iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Modern Web

製作你的無程式碼(No-code)個人網頁 ft. Next.js, SWR, 串 Youtube, IG, Github, Notion API ~系列 第 25

#25 No-code 之旅 — 實作 Notion 部落格 Pagination (分頁) 功能 ft. SWR

嗨大家!像昨天說的,今天會講怎麼用 SWR 實作 Notion 部落格的 pagination (分頁) 功能~ 還沒看昨天的文章的大家,可以點這裡,今天的文章會用到昨天的 API Route 喔!還不知道什麼是 SWR 的大家也可以看這篇

Pagination

API Route

在昨天的文章我們開了一個新的 API,路徑為 /api/blogs,這 API 會回傳某 Notion database 的內容 (部落格文章):

// pages/api/blogs.js
import { queryDatabase } from "lib/notion";

export default async function handler(req, res) {
  // pagination 的 cursor
  const { cursor } = req.query;
  // 去 query Notion database  
  const resp = await queryDatabase({ start_cursor: cursor });

  // 用 Next.js 提供的 response helper 回傳 JSON 格式的 `resp`
  res.json(resp);
}

getStaticProps

在使用 API Route 之前,我們先去 pages/blogs.js 裡寫 getStaticProps 的 data fetching 方式,來抓取資料~

// pages/blogs.js
export const getStaticProps = async () => {
  // 去 query Notion database
  const blogs = await queryDatabase();

  return {
    props: { fallback: { "/api/blogs": blogs } },
    revalidate: 60, // 過了 60 秒後至少會更新一次資料
  };
};

看了上面的 code 之後,可能會發現跟之前的寫法不太一樣。原本應該可以直接回傳 props: { blogs },可是今天是回傳 props: { fallback: { "/api/blogs": blogs } }。為什麼呢?

  • 這麼做的原因是想要用 SWR 在 client-side 抓取資料,
  • 不過不希望頁面的 build 的過程拿不到資料導致產生的 HTML 檔案沒有內容
  • 所以在 build 時候先抓取資料,然後把這些資料給該 page 的 props
  • fallback 物件包含各 SWR key 的備份資料 (沒內容時的預設資料)
  • 其中一個 key/api/blogs 就是我們 API Route 的路徑
  • 想了解更多可以看這篇Pre-rendering 小段

SWRConfig

在上一段已經拿完的資料,該怎麼提供給 components 去顯示呢?該怎麼給 useSWR 當 fallback 料?答案就是透過 SWRConfig 的 context!

const Blogs = ({ fallback }) => {
  return (
    <main>
      // 用 SWRConfig 去包需要用到 fallback 資料的 components
      <SWRConfig value={{ fallback }}>
        // 部落個文章的 list
        <List />
      </SWRConfig>
    </main>
  );
};

SWRConfig 裡的所有 components 可以用 useSWR 拿到 fallback 資料~

Infinite Loading

SWR 提供兩種 pagination 模式,一個是一般的上下頁那種,另一種是可以一直按 load more 的 infinite loading。今天這篇文是用 useSWRInfinite,也就是頁面可以一直往下滑的那種,用法跟一般的 useSWR 類似:

import useSWRInfinite from 'swr/infinite'

const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
  getKey, fetcher?, options?
)

不過要注意的是他第一個 parameter 不是 key 而是 getKey,一個回傳 key 的 function:

const getKey = (pageIndex, previousPageData) => {
  if (previousPageData && !previousPageData.length) return null // 到最後一頁了
  return `/api/blogs?page=${pageIndex}` // 回傳 SWR key
}

getKey

上面的 getKey function 只是例子,而且他是用 pageIndex 做分頁的,可是 Notion SDK 是用 cursor 做分頁,所以我們可以寫一個 cursor based 的 getKey

const getKey = (pageIndex,previousData) => {
  if (pageIndex === 0) {
    return "/api/blogs"; // 第ㄧ頁
  }
  return previousData && previousData.has_more && previousData.next_cursor
    ? `/api/blogs?cursor=${previousData.next_cursor}` // key 加 cursor
    : null; // 到底
};

List component

現在可以寫 component 來顯示資料了!

const List = () => {
  const { data, size, setSize } = useSWRInfinite(getKey, fetcher);

  if (!data) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      // array of API responses
      {data.map((blogs) =>
        // results 為 page list
        blogs.results.map((page) => (
          <Box key={page.id}>
            <AvatarComponent size={80} user={page.properties.Owner} />
            // Notion page 的 Title 資料格式比較複雜XD
            {page.properties.Title.type === "title" &&
              page.properties.Title.title.map((t) => t.plain_text).join(" ")}
          </Box>
        ))
      )}
      <Button onClick={() => setSize(size + 1)}>
        Load more
      </Button>
    </div>
  );
};

這裡要注意,如果用 useSWR 的話,data 會是 API 回傳的資料,也就是 data == blogs == { results }。可是因為我們用 useSWRInfinitedata 變成是 array (陣列) 格式,裡面包含很多個 API response 為一頁,

// data
[
  API response 1, // 第一頁
  API response 2, // 第二頁
  ...
]

所以需要用兩個回圈去顯示資料喔!最後給大家看成果~

Load more

小結

耶~ 終於做完部落格的分頁功能了,而且今天寫得還滿順利,SWR 配 Next.js 的 API Routes 真的很讚!而且用 SWR 資料會自動被 cache,所以只有第一次 loading 會比較久。大家有什麼問題都可以問喔~

大家想要看看之前的網站可以看這裡,或是直接到首頁~ 有任何問題可以問我,或是也可以討論未來要開發的 No-code 專案喔。

祝大家連假愉快!

午安 <3

看更多


上一篇
#24 No-code 之旅 — 在 Next.js 專案中實作 API
下一篇
#26 No-code 之旅 — 實作 Dark Mode 和加入 Google Fonts ft. Chakra UI
系列文
製作你的無程式碼(No-code)個人網頁 ft. Next.js, SWR, 串 Youtube, IG, Github, Notion API ~30

尚未有邦友留言

立即登入留言