11import { apiBlog } from '@ts-rest/example-contracts' ;
22import { initQueryClient } from '@ts-rest/react-query' ;
33import Link from 'next/link' ;
4- import { useEffect } from 'react' ;
5- import { useDebounce } from '../hooks/useDebounce' ;
6- import { useStore } from '../state' ;
4+ import classNames from 'classnames' ;
75
86export const api = initQueryClient ( apiBlog , {
9- baseUrl : 'http://localhost:3334 ' ,
7+ baseUrl : 'http://localhost:4200/api ' ,
108 baseHeaders : { } ,
119} ) ;
1210
1311export function Index ( ) {
14- const { searchString } = useStore ( ) ;
12+ const PAGE_SIZE = 5 ;
1513
16- const { data, isLoading, refetch } = api . getPosts . useQuery ( [ 'posts' ] , {
17- query : {
18- take : 5 ,
19- skip : 0 ,
20- ...( searchString !== '' ? { search : searchString } : { } ) ,
21- } ,
22- } ) ;
23-
24- const searchStringDebounced = useDebounce ( searchString , 250 ) ;
25-
26- useEffect ( ( ) => {
27- refetch ( ) ;
28- } , [ refetch , searchStringDebounced ] ) ;
14+ const { isLoading, data, hasNextPage, fetchNextPage } =
15+ api . getPosts . useInfiniteQuery (
16+ [ 'posts' ] ,
17+ ( { pageParam = { skip : 0 , take : PAGE_SIZE } } ) => ( {
18+ query : { skip : pageParam . skip , take : pageParam . take } ,
19+ } ) ,
20+ {
21+ getNextPageParam : ( lastPage , allPages ) =>
22+ lastPage . status === 200
23+ ? lastPage . body . count > allPages . length * PAGE_SIZE
24+ ? { take : PAGE_SIZE , skip : allPages . length * PAGE_SIZE }
25+ : undefined
26+ : undefined ,
27+ }
28+ ) ;
2929
3030 if ( isLoading ) {
3131 return < div > Loading...</ div > ;
@@ -35,36 +35,47 @@ export function Index() {
3535 return < div > No posts found</ div > ;
3636 }
3737
38- const { posts } = data . body ;
38+ const posts = data . pages . flatMap ( ( page ) =>
39+ page . status === 200 ? page . body . posts : [ ]
40+ ) ;
3941
4042 return (
41- < div className = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4" >
42- { posts . map ( ( post ) => (
43- < Link href = { `/post/${ post . id } ` } key = { post . id } >
44- < div className = "card bg-base-100 shadow-xl w-full hover:scale-105 transition cursor-pointer" >
45- < div className = "card-body" >
46- < div className = "flex flex-row justify-between" >
47- < h2 className = "card-title" > { post . title } </ h2 >
48- < div >
49- < div className = "avatar placeholder" >
50- < div className = "bg-neutral-focus text-neutral-content rounded-full w-8" >
51- < span className = "text-xs" > OB</ span >
43+ < div >
44+ < div className = "grid grid-cols-1 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4" >
45+ { posts . map ( ( post ) => (
46+ < Link href = { `/post/${ post . id } ` } key = { post . id } >
47+ < div className = "card bg-base-100 shadow-xl w-full hover:scale-105 transition cursor-pointer" >
48+ < div className = "card-body" >
49+ < div className = "flex flex-row justify-between" >
50+ < h2 className = "card-title" > { post . title } </ h2 >
51+ < div >
52+ < div className = "avatar placeholder" >
53+ < div className = "bg-neutral-focus text-neutral-content rounded-full w-8" >
54+ < span className = "text-xs" > OB</ span >
55+ </ div >
5256 </ div >
5357 </ div >
5458 </ div >
55- </ div >
56- < p > { post . description } ? </ p >
57- < div className = "card-actions justify-end" >
58- { post . tags . map ( ( tag ) => (
59- < div key = { tag } className = "badge badge-outline" >
60- Fashion
61- </ div >
62- ) ) }
59+ < p > { post . description } ? </ p >
60+ < div className = "card-actions justify-end" >
61+ { post . tags . map ( ( tag ) => (
62+ < div key = { tag } className = "badge badge-outline" >
63+ Fashion
64+ </ div >
65+ ) ) }
66+ </ div >
6367 </ div >
6468 </ div >
65- </ div >
66- </ Link >
67- ) ) }
69+ </ Link >
70+ ) ) }
71+ </ div >
72+ < button
73+ disabled = { ! hasNextPage }
74+ className = { classNames ( 'btn mt-6' , { 'btn-disabled' : ! hasNextPage } ) }
75+ onClick = { ( ) => fetchNextPage ( ) }
76+ >
77+ Load more
78+ </ button >
6879 </ div >
6980 ) ;
7081}
0 commit comments