Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SP2] 블로그 페이지 스켈레톤 UI, scrollToTop, react-query #251

Merged
merged 17 commits into from
Nov 6, 2023

Conversation

f0rever0
Copy link
Contributor

@f0rever0 f0rever0 commented Oct 31, 2023

Summary

close #250

  • 데이터 받아올때 스켈레톤 UI
  • scrollToTop 옵션 추가
  • 블로그 페이지 게시물 호버시 opacity 0.7로 변경
  • useInfiniteQuery 활용한 react-query 적용

작업을 수행했습니다!

Screenshot

스켈레톤 UI

데스크탑 모바일
image image

react-query
드롭다운 이동간에 이전 무한스크롤 값이 유지되는 것을 확인할 수 있습니다.
전체 -> 기획 -> 전체 ( 이전에 전체드롭다운에서 무한스크롤한 데이터가 유지되어있음 )
https://github.com/sopt-makers/sopt.org-frontend/assets/62867581/0f7c4022-e52b-42ba-9c69-8b2a21cb2a22

Comment

@f0rever0 f0rever0 self-assigned this Oct 31, 2023
import * as S from './style';

export default function BlogPostSkeletonUI() {
const array = new Array(3).fill(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 여기서부터 [0,0,0]으로 �선언해서 뒤에선 .map(_, index)로 key값을 쓰는 것 같은데, 여기서부터 [0,1,2]를 갖게 하는건 어떤가요?
  • array라는 이름보다 명확한 이름이면 더 좋을 것 같아요 ..!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProjectSkeletionUI를 참고해서 만들었는데 그게 더 깔끔한 것 같네요!

<OvalSpinner />
</S.SpinnerWrapper>
{response.data.length === 0 ? (
response._TAG === 'LOADING' && <BlogPostSkeletonUI />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BlogPostLlist.tsx 안에 있는

<S.EmptyBlogPostList>
  {`아직 올라온 ${selectedTap === 'article' ? '아티클이' : '활동후기가'} 없어요`}
</S.EmptyBlogPostList>

이 친구도 여기로 빼면 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 코드 리팩토링 하면서 반영해볼게요!

response._TAG === 'LOADING' && <BlogPostSkeletonUI />
) : (
<>
<BlogPostList
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저의생각))
BlogPostList를 선택적으로 띄울 필요가 없다!!
왜냐면, 위에서 제가 제안한 대로 '아직 올라온 아티클이/활동후기가 없어요' 부분이 여기로 빠진다면,
-> BlogPostList는 response.data를 띄우는 역할만 할 것이고,
-> 그럼 자연스럽게 response.data.length가 0이면 아무것도 안 보일 것이기 때문에
굳이 패칭 시작해서 data.length가 0이 될 때마다 마운트/언마운트를 시키지 않아도 괜찮을 거라고 생각합니다!!!!!!!!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아! BlogPostList는 항상 보여지고 데이터가 없을때만 없다는 안내 문구 를 띄우면 되겠네요..!
조건을 수정해볼게요!! 의견 감사합니다 !!

@pull-request-size pull-request-size bot added size/XL and removed size/L labels Nov 2, 2023
@f0rever0 f0rever0 changed the title [SP3] 블로그 페이지 스켈레톤 UI, scrollToTop [SP3] 블로그 페이지 스켈레톤 UI, scrollToTop, react-query Nov 2, 2023
Copy link
Contributor Author

@f0rever0 f0rever0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정상적으로 작동이 되어...! useInfiniteQuery 를 활용한 무한스크롤을 올립니다!
useInfiniteQuery 를 활용한 무한스크롤 참고 아티클을 참고해서 작업 했습니다!!
작동 화면은 pr 문서에 추가해두었습니다!

if (!hasObserved && entry.isIntersecting && !isLoading) {
setCount((prevCount) => prevCount + 1);
if (!hasObserved && entry.isIntersecting) {
fetchNextPage();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchNextPage() 를 사용해 useGetResponseuseInfiniteQuery 에서 다음 페이지를 불러오는 getNextPageParam: (lastPage) => (lastPage.hasNextPage ? lastPage.currentPage + 1 : undefined), 를 실행합니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getNextPageParam 을 실행하면 hasNextPage === true 일 경우 pageParam 값에 currentPage + 1을 할당합니다.

) => {
const queryKey = [selectedTab, generation, part];

const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useInfiniteQuery 공식 아티클에서 더 자세한 속성 설명을 확인할 수 있습니다.

);

return {
response: data?.pages.flatMap((page) => page.response),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response 값 자체에 현재 페이지까지의 모든 데이터가 담겨서 내려옵니다.

Copy link
Member

@SeojinSeojin SeojinSeojin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무너무너무 수고 많으셨습니다~!~!!

@@ -12,4 +12,6 @@ export function Layout({
const Main = styled.div<{ moreStyle?: SerializedStyles }>`
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100vh;
Copy link
Member

@SeojinSeojin SeojinSeojin Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 추가했을 때 다른 페이지들 깨지지 않는지 확인을 부탁드립니다!!!

여럿이 쓰는 스타일이라서 확인이 필요해요..!!

만약 깨진다면, moreStyles에 넣어주셔요~!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 알겠습니다!

const { data } = await client.get<{
hasNextPage: boolean;
data: BlogPostType[];
currentPage: number;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 있는거 타입으로 빼면 어떤가요??

BlogPostType을 제너릭 인자로 받아오는 .. 그런 베이스 타입을 선언해두면 나중에 두고두고 쓰기 좋을 것 같아요!!

@@ -4,6 +4,7 @@ import { BlogPostType } from './blog';
export type GetSopticlesResponse = {
hasNextPage: boolean;
response: BlogPostType[];
currentPage: number;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 솝티클 모두 위 코멘트와 마찬가지로 response 를 T로 갖는 새 타입 하나 지정해주시고 같이 쓰면 좋을 것 같아요~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음 하나의 타입으로 분리해볼게요!

} 전체 보기`}</S.Total>
</S.EmptyBlogPostListWrapper>
)
) : (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전에 코멘트 드렸던 대로 이 부분은 항상 삼항 연산자 밖에 있어도 괜찮을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번에 작업하면서 다시 안으로 들어갔나봐요....😂 다시 조건문 고민해볼게요!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeojinSeojin 서진 ~ 이부분 코멘트 좀 더 자세히 해줄 수 있을까요?
위 코멘트도 다시 읽어봤는데...!

저의생각))
BlogPostList를 선택적으로 띄울 필요가 없다!!
왜냐면, 위에서 제가 제안한 대로 '아직 올라온 아티클이/활동후기가 없어요' 부분이 여기로 빠진다면,
-> BlogPostList는 response.data를 띄우는 역할만 할 것이고,
-> 그럼 자연스럽게 response.data.length가 0이면 아무것도 안 보일 것이기 때문에
굳이 패칭 시작해서 data.length가 0이 될 때마다 마운트/언마운트를 시키지 않아도 괜찮을 거라고 생각합니다!!!!!!!!!

여기서 BlogPostList를 선택적을 띄울 필요가 없는 것은 이해하겠는데, 아직 올라온 아티클이/활동후기가 없어요' 게 어디로 빠지면 좋겠다는지 이해가 잘 안돼서요..!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전에는 문구가 blogPostList 컴포넌트 아래에 있었는데, 그것의 부모 컴포넌트인 이곳으로 오면 좋겠다는 뜻이었어요..!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아,..! 저렇게 두어도 좋겠다는 뜻이었구뇨! 이해했습니다 !!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 이 부분 의도하신 흐름에 대해서 말로 풀어서 설명해주실 수 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

      {!response ? (
        <BlogPostSkeletonUI />
      ) : (
        <>
          {response.length === 0 ? (
            <S.EmptyBlogPostListWrapper>
              <S.EmptyBlogPostList>
                {`아직 올라온 ${selectedTab === 'article' ? '아티클이' : '활동후기가'} 없어요`}
              </S.EmptyBlogPostList>
              <S.Total onClick={showTotalPostList}>{`${
                selectedTab === 'article' ? '아티클' : '활동후기'
              } 전체 보기`}</S.Total>
            </S.EmptyBlogPostListWrapper>
          ) : (
            <>
              <BlogPostList selectedTap={selectedTab} blogPostList={response} />
              {(hasNextPage || isFetching) && (
                <S.SpinnerWrapper ref={hasNextPage ? ref : undefined}>
                  <OvalSpinner />
                </S.SpinnerWrapper>
              )}
            </>
          )}
        </>
      )}

이렇게 수정하였습니다!
데이터가 ㅐ처음 패칭중일때 undefined를 반환해서 그때는 BlogPostSkeletonUI 를 보여주고
response.length === 0 으로 아티클이 없을때는 없다는 문구가, !== 0으로 있을때는 리스트가 보여지게 됩니다!

))}
</S.BlogPostList>
)}
<S.BlogPostList>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapper이랑 요거랑 하나로 합칠 수 있을 것 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혜준언니가 작업한거라 스타일 하나를 수정하면 연달아 다 수정해야할 것 같아서 시도는 해볼게요!

: getArticleResponse(generation, part, count);
};

export const useGetResponse = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좀 더 무한스크롤 관련한 훅이라는 느낌이 났으면 좋겠어요..!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

무한스크롤은 useInfiniteScroll 에서 하고 이 부분은 API 응답을 받아오는 쿼리문이라서 useGetResponse로 지었어요!
더 적절한거면 useGetInfiniteResponse 요런 걸까요?!!

export default function useInfiniteScroll(isLoading: boolean) {
const [count, setCount] = useState(1);
export default function useInfiniteScroll(fetchNextPage: {
(options?: FetchNextPageOptions | undefined): Promise<
Copy link
Member

@SeojinSeojin SeojinSeojin Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옵셔널로 받으면 | undefined를 쓰지 않아도 될 것 같아요!

(options?: FetchNextPageOptions) 
(options: FetchNextPageOptions | undefined)

둘 중에 하나만 써도 괜찮을 것 같아요!

const [count, setCount] = useState(1);
export default function useInfiniteScroll(fetchNextPage: {
(options?: FetchNextPageOptions | undefined): Promise<
InfiniteQueryObserverResult<GetReviewsResponse, unknown>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unknown이 들어가는 이유가 무엇인가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분... vscode의 타입추론을 해준 것인데...ㅎㅎ 위에 서진의 코멘트랑 해서 바꿔볼게요!

Copy link
Member

@solar3070 solar3070 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 react-query 버전 v4로 업데이트하는 거 어떻게 생각하시나요?!
음 어느새 버전5가 나왔네요.. 일단 이대로 하고 나중에 업데이트에 대해 이야기해봅시다..!

@f0rever0
Copy link
Contributor Author

f0rever0 commented Nov 4, 2023

혹시 query v4에서 사용하고 싶은 기능이 현재 작업중에 있으실까요? 없으면 블로그, 프로젝트 탭이랑 다 머지하고 버전 한번에 올리는 건 어떨까요?

@f0rever0 f0rever0 changed the title [SP3] 블로그 페이지 스켈레톤 UI, scrollToTop, react-query [SP2] 블로그 페이지 스켈레톤 UI, scrollToTop, react-query Nov 5, 2023

if (!(response._TAG === 'OK' || response._TAG === 'LOADING')) return null;
if (!response) return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null을 return 하다 보니 데이터가 내려오기 전까지 탭을 전환하거나 필터 전환, 새로고침 등을 했을 때 잠깐 검은 화면이 보이게 됩니다. 사용성이 좋지 않다고 느껴져서 개선하면 좋을 것 같습니다!

1.mp4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 query 결과로 respons가 undefined가 내려오는 경우를 예외처리한건데ㅔ, v5로 업데이트하면 undefiend가 반환되지 않아서 그때 제거하면 될 거같아요!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서스펜스쿼리는 그렇다고 알고 있는데 useInfiniteQuery도 데이터가 무조건 define되어서 내려오나요?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터 fetching 전에 undefined 가 뜹니다!
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 v5로 업데이트 했을 때요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공식문서에 return 값에 대한 특징이 useQuery와 동일하게 가져간다고 나와있습니다!
image
v5공식문서

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했는데 v5 useQuery도 data 기본값이 undefined인 것 같아요..!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 제가 착각했네요...! 해당 내용말고 useInfiniteQuery와 동일한 기능을하는데 혜준언니가 프로젝트 탭에서 말해준 서스펜스 기능을하는 useSuspenseInfiniteQuery
을 봤습니다!
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에 조건문 수정하면서 해당 문제도 수정했습니다!

2023-11-05.11.06.25.mov

@f0rever0 f0rever0 merged commit 47eacce into develop Nov 6, 2023
1 check passed
@f0rever0 f0rever0 deleted the feat/#250-blogPage branch November 6, 2023 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

리프레시 기간 블로그 추가 개발
3 participants