Skip to content

Commit b8e83da

Browse files
committed
feat: useScrollPosition 사용하여 스크롤 유지
1 parent 9e8ab22 commit b8e83da

4 files changed

Lines changed: 66 additions & 54 deletions

File tree

src/pages/MainPage/PostBody.jsx

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,52 @@ import theme from 'styles/theme';
88
import { setLike, setDisLike } from 'utils/apis/postApi';
99
import { setNotification } from 'utils/apis/userApi';
1010
import useLocalToken from 'hooks/useLocalToken';
11+
import useScrollPosition from 'hooks/useScrollPosition';
1112
import { useUserContext } from 'contexts/UserContext';
1213
import { IMAGE_URLS } from 'utils/constants/images';
1314
import displayedAt from 'utils/functions/displayedAt';
1415

15-
const PostBody = ({ post, isDetailPage = false }) => {
16+
const PostBody = ({ index, post, isDetailPage = false }) => {
1617
const { _id: postId, image, likes, comments, createdAt, author } = post || {};
17-
const { content, contents, tags } = JSON.parse(post?.title);
18+
const { content, tags } = JSON.parse(post?.title);
1819
const [onHeart, setOnHeart] = useState(false);
1920
const [heartCount, setHeartCount] = useState(likes.length);
2021
const [likeId, setLikeId] = useState('');
2122
const [isShown, setIsShown] = useState(false);
2223
const [localToken] = useLocalToken();
2324
const { currentUser } = useUserContext();
24-
25+
const [, setCurrentPostIndex] = useScrollPosition();
2526
const navigate = useNavigate();
2627

27-
const handleTodetailpage = useCallback(() => {
28+
useEffect(() => {
29+
let likeId;
30+
const isMyLikePost =
31+
likes.filter(({ user, _id }) => {
32+
if (user === currentUser.id) {
33+
likeId = _id;
34+
return true;
35+
}
36+
}).length > 0;
37+
if (isMyLikePost) {
38+
setOnHeart(true);
39+
setLikeId(likeId);
40+
}
41+
}, [currentUser, likes]);
42+
43+
const navigateToDetailPage = useCallback(() => {
2844
if (isDetailPage) {
2945
return;
3046
}
47+
setCurrentPostIndex(index + 1);
3148
navigate(`/post/detail/${postId}`, {
3249
state: {
3350
post,
51+
index,
3452
},
3553
});
36-
}, [postId, post, isDetailPage, navigate]);
54+
}, [setCurrentPostIndex, index, postId, post, isDetailPage, navigate]);
3755

38-
const handleTagClick = useCallback(
39-
(tag) => {
40-
navigate(`/tag/${tag.slice(1)}`, {
41-
state: {
42-
tag,
43-
},
44-
});
45-
},
46-
[navigate],
47-
);
48-
49-
const handleHeartClick = useCallback(async () => {
56+
const handleClickHeart = useCallback(async () => {
5057
setOnHeart(!onHeart);
5158
if (!onHeart) {
5259
setHeartCount(heartCount + 1);
@@ -66,28 +73,25 @@ const PostBody = ({ post, isDetailPage = false }) => {
6673
}
6774
}, [onHeart, heartCount, postId, likeId, author._id, currentUser, localToken]);
6875

69-
useEffect(() => {
70-
let likeId;
71-
const isMyLikePost =
72-
likes.filter(({ user, _id }) => {
73-
if (user === currentUser.id) {
74-
likeId = _id;
75-
return true;
76-
}
77-
}).length > 0;
78-
if (isMyLikePost) {
79-
setOnHeart(true);
80-
setLikeId(likeId);
81-
}
82-
}, [currentUser, likes]);
76+
const handleClickTag = useCallback(
77+
(tag) => {
78+
setCurrentPostIndex(index + 1);
79+
navigate(`/tag/${tag.slice(1)}`, {
80+
state: {
81+
tag,
82+
},
83+
});
84+
},
85+
[index, setCurrentPostIndex, navigate],
86+
);
8387

84-
const handleMoreClick = () => {
88+
const handleClickMore = () => {
8589
setIsShown(true);
8690
};
8791

8892
return (
8993
<Container>
90-
<ImageWrapper onClick={handleTodetailpage} isDetailPage={isDetailPage}>
94+
<ImageWrapper onClick={navigateToDetailPage} isDetailPage={isDetailPage}>
9195
<Image
9296
src={image ? image : IMAGE_URLS.POST_DEFAULT_IMG}
9397
defaultImageUrl={IMAGE_URLS.POST_DEFAULT_IMG}
@@ -98,13 +102,12 @@ const PostBody = ({ post, isDetailPage = false }) => {
98102
<Contents>
99103
<IconButtons>
100104
<IconButton
101-
className="heart-button"
102105
name={onHeart ? 'HEART_RED' : 'HEART'} // Todo: 상수화
103-
onClick={handleHeartClick}
106+
onClick={handleClickHeart}
104107
>
105108
<IconButtonText>{heartCount}</IconButtonText>
106109
</IconButton>
107-
<IconButton className="comment-button" name="COMMENT" onClick={handleTodetailpage}>
110+
<IconButton name="COMMENT" onClick={navigateToDetailPage}>
108111
<IconButtonText>{comments.length}</IconButtonText>
109112
</IconButton>
110113
</IconButtons>
@@ -113,12 +116,12 @@ const PostBody = ({ post, isDetailPage = false }) => {
113116
{content}
114117
</Paragraph>
115118
{!isDetailPage && content?.length > 50 && !isShown && (
116-
<MoreText onClick={handleMoreClick}>더보기</MoreText>
119+
<MoreText onClick={handleClickMore}>더보기</MoreText>
117120
)}
118121
</TextContainer>
119122
<Tags>
120123
{tags.map((tag, i) => (
121-
<Tag key={i} onClick={() => handleTagClick(tag)}>
124+
<Tag key={i} onClick={() => handleClickTag(tag)}>
122125
{tag[0] === '#' ? tag : `#${tag}`}
123126
</Tag>
124127
))}
@@ -155,7 +158,7 @@ const IconButtons = styled.div`
155158
display: flex;
156159
`;
157160

158-
const IconButton = ({ name, className, children, onClick }) => {
161+
const IconButton = ({ name, children, onClick }) => {
159162
const style = {
160163
padding: 0,
161164
borderRadius: '0',
@@ -167,7 +170,7 @@ const IconButton = ({ name, className, children, onClick }) => {
167170
color: theme.color.fontBlack,
168171
};
169172
return (
170-
<button className={className} style={style} onClick={onClick}>
173+
<button style={style} onClick={onClick}>
171174
<Icon name={name} size={22} />
172175
{children}
173176
</button>

src/pages/MainPage/PostItem.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import PostBody from './PostBody';
33
import styled from '@emotion/styled';
44
import theme from 'styles/theme';
55

6-
const PostItem = ({ post, isDetailPage }) => {
6+
const PostItem = ({ index, post, isDetailPage }) => {
77
return (
88
<Article>
99
<PostHeader post={post} isDetailPage={isDetailPage} />
10-
<PostBody post={post} isDetailPage={isDetailPage} />
10+
<PostBody index={index} post={post} isDetailPage={isDetailPage} />
1111
</Article>
1212
);
1313
};

src/pages/MainPage/index.jsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,60 @@ import { PageWrapper } from 'components';
33
import { getPostsPart } from 'utils/apis/postApi';
44
import PostItem from './PostItem';
55
import { useState, useEffect, useCallback, useRef } from 'react';
6+
import useScrollPosition from 'hooks/useScrollPosition';
67

78
const LIMIT = 5;
89

910
const MainPage = () => {
1011
const [posts, setPosts] = useState([]);
11-
const [isLoding, setIsLoding] = useState(false);
12+
const [isLoading, setIsLoading] = useState(false);
1213
const [offset, setOffset] = useState(0);
1314
const [max, setMax] = useState(0);
1415
const targetRef = useRef(null);
16+
const [prevPostIndex, setPrevPostIndex] = useScrollPosition();
1517

1618
useEffect(() => {
19+
const limit = prevPostIndex ? prevPostIndex : LIMIT;
1720
(async () => {
1821
const nextPosts = await getPostsPart({
1922
offset,
20-
limit: LIMIT,
23+
limit,
2124
}).then((res) => res.data);
2225
setPosts(nextPosts);
23-
setOffset(offset + 5);
2426
setMax(nextPosts[0].channel.posts.length);
27+
setOffset(prevPostIndex ? prevPostIndex : 5);
2528
})();
2629
}, []);
2730

31+
useEffect(() => {
32+
if (targetRef.current && prevPostIndex) {
33+
window.scrollTo(0, document.body.scrollHeight);
34+
setPrevPostIndex(0);
35+
}
36+
}, [targetRef, prevPostIndex, setPrevPostIndex]);
37+
2838
const onIntersect = useCallback(
2939
async ([entry], observer) => {
30-
if (entry.isIntersecting && !isLoding && offset < max) {
40+
if (entry.isIntersecting && !isLoading && offset < max) {
3141
observer.disconnect();
32-
setIsLoding(true);
42+
setIsLoading(true);
3343
setOffset(offset + 5);
3444
const nextPosts = await getPostsPart({
3545
offset,
3646
limit: LIMIT,
3747
}).then((res) => res.data);
3848
setPosts([...posts, ...nextPosts]);
39-
setIsLoding(false);
49+
setIsLoading(false);
4050
}
4151
},
42-
[isLoding, offset, max, posts],
52+
[isLoading, offset, max, posts],
4353
);
4454

4555
useEffect(() => {
4656
let observer;
4757
if (targetRef.current) {
4858
observer = new IntersectionObserver(onIntersect, {
49-
threshold: 0.4,
59+
threshold: 0.5,
5060
});
5161
observer.observe(targetRef.current);
5262
}
@@ -60,13 +70,13 @@ const MainPage = () => {
6070
if (posts.length - 1 === i) {
6171
return (
6272
<li key={i} ref={targetRef}>
63-
<PostItem key={i} post={post} />
73+
<PostItem key={i} index={i} post={post} />
6474
</li>
6575
);
6676
} else {
6777
return (
6878
<li key={i}>
69-
<PostItem key={i} post={post} />
79+
<PostItem key={i} index={i} post={post} />
7080
</li>
7181
);
7282
}

src/pages/PostDetailPage/index.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Avatar, Icon, Modal, PageWrapper } from 'components';
44
import theme from 'styles/theme';
55
import { useRef, useCallback, useState, useEffect } from 'react';
66
import { addComment } from 'utils/apis/postApi';
7-
import { IMAGE_URLS } from 'utils/constants/images';
87
import useLocalToken from 'hooks/useLocalToken';
98
import { useLocation, useNavigate } from 'react-router-dom';
109
import { getPostData, deleteComment } from 'utils/apis/postApi';
@@ -97,7 +96,7 @@ const PostDetailPage = () => {
9796
{post && (
9897
<PageWrapper header nav prev title={post.author.fullName + '님의 게시물'}>
9998
<Container>
100-
<PostItem post={post} isDetailPage={true} />
99+
<PostItem post={post} isDetailPage={true} index={location.state?.index} />
101100
<CommentInputForm onSubmit={handleSubmit}>
102101
<CommentInput
103102
ref={inputRef}

0 commit comments

Comments
 (0)