iT邦幫忙

2023 iThome 鐵人賽

DAY 1
0
Modern Web

一些讓你看來很強的全端- trcp 伴讀系列 第 16

Day-016. 一些讓你看來很強的全端 TRPC 伴讀 -InfiniteQuery (下)

  • 分享至 

  • xImage
  •  

今天要來優化我們infinite scroll 的內容

新增 post 後下方內容沒有更新

如畫面所見新增資料後,每次都要重新整理你的資料才能重新獲取,那怎麼辦?
大家如果還記得的話前幾天有提到關於 react query 中有個 query cache 概念,所以我們需要在 create post 成功後 updateinfinitePosts 內容。

原本我們是透過 getPosts 顯示 post 資料所以我們 update 的 query cache是 getPosts 內容,但因為現在改成 infinitePosts ,所以需要改成對
infinitePostsrefetch 如下 :

export const PostForm = ({  }: PostFormProps) => {
  const utils = api.useContext()
  const { mutateAsync: createPost } = api.posts.addPost.useMutation({
    onSuccess: ({ post: newPost }) => {
      // utils.posts.getPosts.invalidate()
      utils.posts.infinitePosts.refetch()

如此一來就達到我們要的效果了 ~

新增 post 後滾輪跑到最上面

假設有 userposts lists 到一半時突然想新增 post ,但這時你會發現 user 第一時間不會知道自己有沒有成功創建資料,當然你可能想說可以做 toast 提醒,這沒問題,不過如果 user 想知道最新一筆新增的資料就需要一直往上滑才會看到,使用上會非常麻煩。

所以我們希望可以完成每當 user 新增一筆資料滾輪就重新滑到最上面,除了可以增加互動外, user 也不需要一個一個往上滑找內容了。

首先宣告 bottomOfPanelRef 給需要監聽的 div

const bottomOfPanelRef = useRef<HTMLDivElement | null>(null)

實作 scroll to topfunction,滾輪效果選擇 smooth

 const scrollToBottom = () => {
    if (!bottomOfPanelRef.current) return
    bottomOfPanelRef.current.scrollTo({ top: 0, behavior: "smooth" })
  }

bottomOfPanelRef 放到最外層實現 overflow-y-scrolldiv

<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md flex flex-col gap-[2rem]">
        <PostForm  />
        <div className="max-h-[300px] overflow-y-scroll" ref={bottomOfPanelRef}>

因為我們滑動小果會是在 create post 成功後直接,所以需要把 scrollToBottom 當成 props 傳給 PostForm

<PostForm updateSuccessCallBack={scrollToBottom} />

addPost 成功時同步執行 infinitePosts.refetchupdateSuccessCallBack

export const PostForm = ({ updateSuccessCallBack }: PostFormProps) => {
  const utils = api.useContext()
  const { mutateAsync: createPost } = api.posts.addPost.useMutation({
    onSuccess: ({ post: newPost }) => {
      // utils.posts.getPosts.invalidate()
      utils.posts.infinitePosts.refetch()
      console.log('onSuccess')
      if (updateSuccessCallBack) {
        updateSuccessCallBack()
      }
    },
//..

這樣就完成功能拉~

post 添加 filter

另外一個添加功能是假如今天 post 資料太多對 user 來說可能不好找到他們要的資料,這時就需要加 filter 功能,

input 這邊我我簡單做了 useDebounce 功能,詳細內容大家可以看 code 就好這邊就不多做敘述。

const [filterValue, setFilterValue] = useState<string>('') 
const handelSearchText = (value: string) => {
    setFilterValue(value)
    // console.log(value)
  }
  return (
    <div className="bg-gray-100 min-h-screen overflow-y-auto p-4">
      <h2 className="text-center text-3xl">Create posts</h2>
      <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md flex flex-col gap-[2rem]">
        <PostForm updateSuccessCallBack={scrollToBottom} />
        <SearchInput value={filterValue} onChange={handelSearchText} />

最後在 infinitePosts 調用 select 根據 filterValue filter 你要的結果。

使用 selectfilter 功能的好處就是,不需要重新打 api 直接從 query cachereturn 你要的資料就好。

 const { data, isLoading, isError, error, fetchNextPage, hasNextPage } = api.posts.infinitePosts.useInfiniteQuery(
    {
      limit: 10,
    },
    {
      getNextPageParam: (lastPage) => lastPage.nextCursor,
      select: (data) => {
        const checkColumns: (keyof Post)[] = ['title']
        return {
          ...data,
          pages: data.pages.map(page => ({
            ...page,
            posts: page.posts.filter(post => checkColumns.some(k => String(post[k]).toLowerCase().includes(filterValue.toLowerCase())))
          }))
        }
      }
    }
  )

但如果你的需求是針對所有 post 資料做 query的話,select 也許就不適合,select 只是根據 query cache 中已經滑到的內容做塞選,所以如果是希望從資料庫中拿取的話,也許用 where 比較適合。

 const { data, isLoading, isError, error, fetchNextPage, hasNextPage } = api.posts.infinitePosts.useInfiniteQuery(
    {
      limit: 10,
      where: {
        content: filterValue
      }
    },
    {
      getNextPageParam: (lastPage) => lastPage.nextCursor,
    }
  )

備註 : where 是自己定義的 input query ,透過 contains 做到相似查詢。

export const postsRouter = router({
  infinitePosts: publicProcedure
    .input(getInfinitePostSchema)
    .query(async ({ input, ctx }) => {
      const { cursor, where } = input
      const { prisma } = ctx
      const limit = input.limit ?? 50
      const posts = await prisma.post.findMany({
        where: {
          title: {
            contains: where?.title
          },
          content: {
            contains: where?.content
          },
        },
        orderBy: {
          id: 'desc'
        },
        take: limit + 1,
        cursor: cursor ? { id: cursor } : undefined
      })

那因為 input 有用到 debounce 功能所以會卡卡的,如果讀者不想要的話可以自行拿掉功能~

目前為止我們就簡單完成 infinite Scroll 的優化摟~ 如果讀者有什麼想法可以在底下留言討論討論~

相關連結 :

https://github.com/Danny101201/next_demo/tree/main

✅ 前端社群 :
https://lihi3.cc/kBe0Y


上一篇
Day-015. 一些讓你看來很強的全端 TRPC 伴讀 -InfiniteQuery (中)
下一篇
Day-017. 一些讓你看來很強的全端 TRPC 伴讀 -Next-Auth( install )
系列文
一些讓你看來很強的全端- trcp 伴讀30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言