#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

看更多


<<:  Day 26 输出输入设备

>>:  Day26 [实作] 一对一视讯通话(6): 关闭镜头或麦克风

轻松小单元 - 最新修正内容(2021)

资安法与时俱进,但也很少会突然多了新应办事项。在草案期间就会公布新内容并举办巡回说明。实际施行也会给...

Day4 专案架构

之後会以create-react-app建立的专案,来进行React後续的学习, 来看看建立後专案的...

替代网站(Alternative Sites)- 冷站点的最大好处

冷站点没有适当的计算机设备,因此它不提供异地数据存储、保留替代计算能力或响应电子发现请求。 冷站点是...

[DAY21] 非同步的 Boxenn Use Case

在实务中,常常会有花很长执行时间、或需要排程的逻辑,这时候便会需要进行非同步处理。 在 Boxenn...

Day2 Visual Studio Code 功能简易说明

Visual Studio Code(简称VS Code) 由微软开发,并且支援Windows、Li...