Skip to content

Commit 17d2741

Browse files
committed
Update: migrate to react-query
1 parent b9385b4 commit 17d2741

2 files changed

Lines changed: 83 additions & 70 deletions

File tree

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,75 @@
11
// hashtag/[tag].js
2+
import { GetStaticPropsContext } from 'next';
23
import React, { useEffect } from 'react';
3-
import { useDispatch, useSelector } from 'react-redux';
4+
import { useInView } from 'react-intersection-observer';
5+
import { dehydrate, QueryClient, useInfiniteQuery } from 'react-query';
46
import { useRouter } from 'next/router';
5-
import { END } from 'redux-saga';
67

7-
import axios from 'axios';
8-
import { LOAD_HASHTAG_POSTS_REQUEST } from '../../reducers/post';
8+
import { loadHashtagPostsAPI } from '../../apis/post';
9+
import Post from '../../interfaces/post';
910
import PostCard from '../../components/PostCard';
10-
import wrapper from '../../store/configureStore';
11-
import { LOAD_MY_INFO_REQUEST } from '../../reducers/user';
1211
import AppLayout from '../../components/AppLayout';
1312

1413
const Hashtag = () => {
15-
const dispatch = useDispatch();
14+
const [ref, inView] = useInView();
1615
const router = useRouter();
1716
const { tag } = router.query;
18-
const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
17+
18+
const {
19+
data,
20+
isLoading: loadPostsLoading,
21+
fetchNextPage,
22+
} = useInfiniteQuery<Post[]>(
23+
['hashtag', tag],
24+
({ pageParam = '' }) => loadHashtagPostsAPI(tag as string, pageParam),
25+
{
26+
getNextPageParam: (lastPage) => {
27+
return lastPage?.[lastPage.length - 1]?.id;
28+
},
29+
},
30+
);
31+
32+
const mainPosts = data?.pages.flat();
33+
const isEmpty = data?.pages[0]?.length === 0;
34+
const isReachingEnd = isEmpty || (data && data.pages[data.pages.length - 1]?.length < 10);
35+
const hasMorePosts = !isEmpty && !isReachingEnd;
36+
const readToLoad = hasMorePosts && !loadPostsLoading;
1937

2038
useEffect(() => {
21-
const onScroll = () => {
22-
if (window.pageYOffset + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
23-
if (hasMorePosts && !loadPostsLoading) {
24-
dispatch({
25-
type: LOAD_HASHTAG_POSTS_REQUEST,
26-
lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.length - 1].id,
27-
data: tag,
28-
});
29-
}
30-
}
31-
};
32-
window.addEventListener('scroll', onScroll);
33-
return () => {
34-
window.removeEventListener('scroll', onScroll);
35-
};
36-
}, [mainPosts.length, hasMorePosts, tag, loadPostsLoading]);
39+
console.log('inView!!!', inView);
40+
if (inView && readToLoad) {
41+
fetchNextPage();
42+
}
43+
}, [inView, readToLoad, fetchNextPage]);
3744

3845
return (
3946
<AppLayout>
40-
{mainPosts.map((c) => (
47+
{mainPosts?.map((c) => (
4148
<PostCard key={c.id} post={c} />
4249
))}
50+
<div ref={readToLoad ? ref : undefined} style={{ height: 50, backgroundColor: 'yellow' }} />
4351
</AppLayout>
4452
);
4553
};
4654

47-
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
48-
const cookie = context.req ? context.req.headers.cookie : '';
49-
console.log(context);
50-
axios.defaults.headers.Cookie = '';
51-
if (context.req && cookie) {
52-
axios.defaults.headers.Cookie = cookie;
55+
export const getStaticProps = async (context: GetStaticPropsContext) => {
56+
const queryClient = new QueryClient();
57+
const tag = context.params?.tag as string;
58+
if (!tag) {
59+
return {
60+
redirect: {
61+
destination: '/',
62+
permanent: true,
63+
},
64+
};
5365
}
54-
context.store.dispatch({
55-
type: LOAD_MY_INFO_REQUEST,
56-
});
57-
context.store.dispatch({
58-
type: LOAD_HASHTAG_POSTS_REQUEST,
59-
data: context.params.tag,
60-
});
61-
context.store.dispatch(END);
62-
await context.store.sagaTask.toPromise();
63-
});
66+
await queryClient.prefetchInfiniteQuery(['hashtag', tag], () => loadHashtagPostsAPI(tag));
67+
68+
return {
69+
props: {
70+
dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))),
71+
},
72+
};
73+
};
6474

6575
export default Hashtag;
Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
// post/[id].js
2+
import { GetStaticPropsContext } from 'next';
23
import React from 'react';
34
import { useRouter } from 'next/router';
4-
import { END } from 'redux-saga';
5-
import axios from 'axios';
6-
import { useSelector } from 'react-redux';
5+
import { dehydrate, QueryClient, useQuery } from 'react-query';
76
import Head from 'next/head';
87

9-
import wrapper from '../../store/configureStore';
10-
import { LOAD_MY_INFO_REQUEST } from '../../reducers/user';
11-
import { LOAD_POST_REQUEST } from '../../reducers/post';
8+
import { loadPostAPI } from '../../apis/post';
9+
import Post from '../../interfaces/post';
1210
import AppLayout from '../../components/AppLayout';
1311
import PostCard from '../../components/PostCard';
1412

15-
const Post = () => {
13+
const SinglePost = () => {
1614
const router = useRouter();
1715
const { id } = router.query;
18-
const { singlePost } = useSelector((state) => state.post);
16+
const { data: singlePost } = useQuery<Post>(['post', id], () => loadPostAPI(Number(id)));
1917

20-
// if (router.isFallback) {
21-
// return <div>로딩중...</div>;
22-
// }
18+
if (!singlePost) {
19+
return <div>존재하지 않는 게시물입니다.</div>;
20+
}
2321

2422
return (
2523
<AppLayout>
@@ -31,7 +29,10 @@ const Post = () => {
3129
<meta name="description" content={singlePost.content} />
3230
<meta property="og:title" content={`${singlePost.User.nickname}님의 게시글`} />
3331
<meta property="og:description" content={singlePost.content} />
34-
<meta property="og:image" content={singlePost.Images[0] ? singlePost.Images[0].src : 'https://nodebird.com/favicon.ico'} />
32+
<meta
33+
property="og:image"
34+
content={singlePost.Images[0] ? singlePost.Images[0].src : 'https://nodebird.com/favicon.ico'}
35+
/>
3536
<meta property="og:url" content={`https://nodebird.com/post/${id}`} />
3637
</Head>
3738
<PostCard post={singlePost} />
@@ -50,22 +51,24 @@ const Post = () => {
5051
// };
5152
// }
5253

53-
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
54-
const cookie = context.req ? context.req.headers.cookie : '';
55-
console.log(context);
56-
axios.defaults.headers.Cookie = '';
57-
if (context.req && cookie) {
58-
axios.defaults.headers.Cookie = cookie;
54+
export const getStaticProps = async (context: GetStaticPropsContext) => {
55+
const queryClient = new QueryClient();
56+
const id = context.params?.id as string;
57+
if (!id) {
58+
return {
59+
redirect: {
60+
destination: '/',
61+
permanent: true,
62+
},
63+
};
5964
}
60-
context.store.dispatch({
61-
type: LOAD_MY_INFO_REQUEST,
62-
});
63-
context.store.dispatch({
64-
type: LOAD_POST_REQUEST,
65-
data: context.params.id,
66-
});
67-
context.store.dispatch(END);
68-
await context.store.sagaTask.toPromise();
69-
});
65+
await queryClient.prefetchQuery(['post', id], () => loadPostAPI(Number(id)));
66+
67+
return {
68+
props: {
69+
dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))),
70+
},
71+
};
72+
};
7073

71-
export default Post;
74+
export default SinglePost;

0 commit comments

Comments
 (0)