Skip to content

Commit 3a7eefd

Browse files
committed
✨ Refactoring useSWRInfiniteScroll
1 parent 5e9e27b commit 3a7eefd

File tree

7 files changed

+88
-63
lines changed

7 files changed

+88
-63
lines changed

app/github/stars/Repos.tsx

+12-19
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
'use client'
22

3+
import InfiniteScrollLoader from "@/components/client/InfiniteScrollLoader"
4+
import useSWRInfiniteScroll from "@/hooks/useSWRInfiniteScroll"
5+
import { SWRInfiniteOptions } from "@/lib/constant"
36
import { Link } from "next-view-transitions"
4-
import { useMemo } from "react"
5-
import useInfiniteScroll from "react-infinite-scroll-hook"
6-
import useSWRInfinite from 'swr/infinite'
7-
import { ForkIcon, IssueIcon, LicenseIcon, Loading, StarIcon } from "../../../components/Icons"
7+
import { ForkIcon, IssueIcon, LicenseIcon, StarIcon } from "../../../components/Icons"
88
import http from "../../../utils/http"
99
import { TRepo } from "./type"
10-
import { SWRInfiniteOptions } from "@/lib/constant"
1110

1211
const limit = 12
1312
const getKey = (pageIndex: number, previousPageData: TRepo[]) => {
14-
if (previousPageData && !previousPageData.length) return null
13+
if (previousPageData && previousPageData.length < limit) return null
1514
return `https://api.github.com/users/AnoyiX/starred?page=${pageIndex + 1}&per_page=${limit}`
1615
}
1716

1817
export function Repos() {
19-
const { data = [], isLoading, error, size, setSize } = useSWRInfinite<TRepo[]>(getKey, http.getAll, SWRInfiniteOptions)
20-
const hasNextPage = useMemo(() => !isLoading && (data.length > 0 && data[data.length - 1]?.length === limit), [isLoading, data])
21-
const [sentryRef] = useInfiniteScroll({
22-
loading: isLoading,
23-
hasNextPage,
24-
onLoadMore: () => setSize(size + 1),
25-
disabled: !!error,
26-
})
18+
const { data, showLoading, sentryRef } = useSWRInfiniteScroll<TRepo[]>(
19+
getKey,
20+
http.getAll,
21+
SWRInfiniteOptions,
22+
data => data.length > 0 && data[data.length - 1]?.length >= limit
23+
)
2724

2825
return (
2926
<>
@@ -73,11 +70,7 @@ export function Repos() {
7370
)))
7471
}
7572
</div>
76-
{(isLoading || hasNextPage) && (
77-
<div ref={sentryRef} className="my-8 mx-auto col-span-full">
78-
<Loading className='h-20 w-20' />
79-
</div>
80-
)}
73+
<InfiniteScrollLoader sentryRef={sentryRef} showLoading={showLoading} />
8174
</>
8275
)
8376
}

app/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Cobe } from "@/components/client/Cobe"
2-
import { Avatar, AvatarImage } from "@/components/ui/avatar"
2+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
33
import { readFileSync } from 'fs'
44
import { Link } from 'next-view-transitions'
55
import path from "path"

app/photo/Photos.tsx

+14-20
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
'use client'
22

33
import ImageSkeleton from "@/components/client/ImageSkeleton"
4-
import { useMemo, useState } from "react"
5-
import useSWRInfinite from 'swr/infinite'
6-
import { Loading, Location } from '../../components/Icons'
4+
import InfiniteScrollLoader from "@/components/client/InfiniteScrollLoader"
5+
import useSWRInfiniteScroll from "@/hooks/useSWRInfiniteScroll"
6+
import { SWRInfiniteOptions } from "@/lib/constant"
7+
import { useState } from "react"
8+
import { Location } from '../../components/Icons'
79
import http from "../../utils/http"
810
import { PageData } from "../../utils/types"
911
import PhotosModal from "./PhotosModal"
1012
import { TPhoto } from "./type"
11-
import useInfiniteScroll from "react-infinite-scroll-hook"
12-
import { SWRInfiniteOptions } from "@/lib/constant"
1313

1414
const limit = 24
1515
const genBody = (page: number) => ({
@@ -23,21 +23,19 @@ const genBody = (page: number) => ({
2323
},
2424
})
2525
const getKey = (pageIndex: number, previousPageData: PageData<TPhoto>) => {
26-
if (previousPageData && !previousPageData.data.length) return null
26+
if (previousPageData && previousPageData.data.length < limit) return null
2727
return [`/api/mongo/find`, genBody(pageIndex)]
2828
}
2929

3030
export function Photos() {
31-
const { data = [], isLoading, error, size, setSize } = useSWRInfinite<PageData<TPhoto>>(getKey, http.post, SWRInfiniteOptions)
32-
const hasNextPage = useMemo(() => !isLoading && (data.length > 0 && data[data.length - 1]?.data.length === limit), [isLoading, data])
33-
const [sentryRef] = useInfiniteScroll({
34-
loading: isLoading,
35-
hasNextPage,
36-
onLoadMore: () => setSize(size + 1),
37-
disabled: !!error,
38-
})
3931
const [photo, setPhoto] = useState<TPhoto | undefined>()
4032
const [showPhotos, setShowPhotos] = useState(false)
33+
const { data, showLoading, sentryRef } = useSWRInfiniteScroll<PageData<TPhoto>>(
34+
getKey,
35+
http.post,
36+
SWRInfiniteOptions,
37+
data => data.length > 0 && data[data.length - 1]?.data.length >= limit
38+
)
4139

4240
const showPhoto = (photo: TPhoto) => {
4341
setPhoto(photo)
@@ -52,7 +50,7 @@ export function Photos() {
5250
<div className="box w-full relative rounded-lg text-xs" onClick={() => showPhoto(photo)} >
5351
<ImageSkeleton src={photo.thumbnail} className="w-full aspect-[5/3] object-cover cursor-pointer rounded-lg" />
5452
<a
55-
className="absolute left-3 top-3 flex flex-fow gap-1 items-center bg-black/50 text-white px-2.5 py-1.5 rounded-full"
53+
className="absolute left-3 bottom-3 flex flex-fow gap-1 items-center bg-black/50 text-white px-2.5 py-1.5 rounded-full"
5654
target="_blank"
5755
rel='noreferrer'
5856
href={photo.address}
@@ -65,11 +63,7 @@ export function Photos() {
6563
)))
6664
}
6765
</div>
68-
{(isLoading || hasNextPage) && (
69-
<div ref={sentryRef} className="my-8 mx-auto col-span-full">
70-
<Loading className='h-20 w-20' />
71-
</div>
72-
)}
66+
<InfiniteScrollLoader sentryRef={sentryRef} showLoading={showLoading} />
7367
<PhotosModal isOpen={showPhotos} photo={photo} onClose={() => setShowPhotos(false)} />
7468
</>
7569
)

app/stock/Lives.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use client'
22

3+
import InfiniteScrollLoader from "@/components/client/InfiniteScrollLoader"
34
import moment from "moment"
45
import 'moment/locale/zh-cn'
56
import { Link } from 'next-view-transitions'
67
import { useCallback, useEffect, useState } from "react"
78
import useInfiniteScroll from "react-infinite-scroll-hook"
89
import useSWR from "swr"
9-
import { Loading } from "../../components/Icons"
1010
import http from "../../utils/http"
1111
import { TLive, TLivesMap, TRealData, TStockInfo } from "./type"
1212

@@ -145,9 +145,7 @@ export default function Lives() {
145145
)
146146
})
147147
}
148-
<div ref={sentryRef} className="my-8 mx-auto col-span-full">
149-
<Loading className='h-20 w-20' />
150-
</div>
148+
<InfiniteScrollLoader sentryRef={sentryRef} showLoading={true} />
151149
</div>
152150
)
153151
}

app/video/Videos.tsx

+11-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
'use client'
22

3-
import { Loading } from "@/components/Icons"
3+
import InfiniteScrollLoader from "@/components/client/InfiniteScrollLoader"
4+
import useSWRInfiniteScroll from "@/hooks/useSWRInfiniteScroll"
45
import { SWRInfiniteOptions } from "@/lib/constant"
5-
import { useMemo, useState } from "react"
6-
import useInfiniteScroll from "react-infinite-scroll-hook"
7-
import useSWRInfinite from 'swr/infinite'
6+
import { useState } from "react"
87
import http from "../../utils/http"
98
import { PageData } from "../../utils/types"
109
import Video from "./Video"
@@ -49,23 +48,20 @@ const genBody = (page: number) => (
4948
}
5049
)
5150
const getKey = (pageIndex: number, previousPageData: PageData<TVideo>) => {
52-
if (previousPageData && !previousPageData.data.length) return null
51+
if (previousPageData && previousPageData.data.length < limit) return null
5352
return [`/api/mongo/find`, genBody(pageIndex)]
5453
}
5554

5655
export default function Videos() {
5756

58-
const { data = [], isLoading, error, size, setSize } = useSWRInfinite<PageData<TVideo>>(getKey, http.post, SWRInfiniteOptions)
59-
const hasNextPage = useMemo(() => !isLoading && (data.length > 0 && data[data.length - 1]?.data.length === limit), [isLoading, data])
60-
const [sentryRef] = useInfiniteScroll({
61-
loading: isLoading,
62-
hasNextPage,
63-
onLoadMore: () => setSize(size + 1),
64-
disabled: !!error,
65-
})
66-
6757
const [showVideo, setShowVideo] = useState(false)
6858
const [vid, setVid] = useState('')
59+
const { data, showLoading, sentryRef } = useSWRInfiniteScroll<PageData<TVideo>>(
60+
getKey,
61+
http.post,
62+
SWRInfiniteOptions,
63+
data => data.length > 0 && data[data.length - 1]?.data.length >= limit
64+
)
6965

7066
const playVideo = (vid: string) => {
7167
setVid(vid)
@@ -79,11 +75,7 @@ export default function Videos() {
7975
data.map(item => item.data.map(video => <Video key={video.aweme_id} video={video} onPlay={playVideo} />))
8076
}
8177
</div>
82-
{(isLoading || hasNextPage) && (
83-
<div ref={sentryRef} className="my-8 mx-auto col-span-full">
84-
<Loading className='h-20 w-20' />
85-
</div>
86-
)}
78+
<InfiniteScrollLoader sentryRef={sentryRef} showLoading={showLoading} />
8779
<VideoModal isOpen={showVideo} vid={vid} onClose={() => setShowVideo(false)} />
8880
</>
8981
)
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use client'
2+
3+
import { cn } from "@/lib/utils";
4+
import { Loading } from "../Icons";
5+
import { IntersectionObserverHookRefCallback } from 'react-intersection-observer-hook';
6+
7+
export interface InfiniteScrollLoaderProps extends React.HTMLAttributes<HTMLDivElement> {
8+
sentryRef: IntersectionObserverHookRefCallback,
9+
showLoading: boolean
10+
}
11+
12+
export default function InfiniteScrollLoader({ showLoading, className, sentryRef, ...props }: InfiniteScrollLoaderProps) {
13+
14+
if (!showLoading) return <></>
15+
16+
return (
17+
<div ref={sentryRef} className={cn("my-8 mx-auto col-span-full", className)} {...props}>
18+
<Loading className='h-20 w-20' />
19+
</div>
20+
)
21+
}

hooks/useSWRInfiniteScroll.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useMemo } from "react"
2+
import useInfiniteScroll from "react-infinite-scroll-hook"
3+
import { BareFetcher } from "swr/_internal"
4+
import useSWRInfinite, { SWRInfiniteConfiguration, SWRInfiniteKeyLoader } from "swr/infinite"
5+
6+
export default function useSWRInfiniteScroll<Data = any, Error = any>(
7+
getKey: SWRInfiniteKeyLoader,
8+
fetcher: BareFetcher<Data> | null,
9+
config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined,
10+
checkHasNextPage: (data: Data[]) => boolean
11+
) {
12+
const { data = [], isLoading, error, size, setSize } = useSWRInfinite<Data>(getKey, fetcher, config)
13+
const hasNextPage = useMemo(() => !isLoading && checkHasNextPage(data), [data, isLoading])
14+
const [sentryRef] = useInfiniteScroll({
15+
loading: isLoading,
16+
hasNextPage,
17+
onLoadMore: () => setSize(size + 1),
18+
disabled: !!error,
19+
})
20+
21+
return {
22+
data,
23+
showLoading: isLoading || hasNextPage,
24+
error,
25+
sentryRef
26+
}
27+
}

0 commit comments

Comments
 (0)