Skip to content

Commit

Permalink
Refactor/#408 SongDetailListPage의 Youtube Player 스크립트 로딩 성능 개선 (#409)
Browse files Browse the repository at this point in the history
* refactor: youtube player 생성 시점을 intersection observer로 제어

1. loading상태에 따른 조건부 preview 이미지 랜더 및 옵저버 트리거로 사용
2. useEffect 제거 및 callbackRef로 대체
3. 옵저버 메모리 누수 방지를 위한 observer ref 생성

* refactor: paint 전 단계에 스크롤 이동 하도록 개선

노래 목록의 첫번째 요소의 player 로드가 되는 버그가 있었음.
첫번째 요소의 preview 이미지가 랜더된 후 스크롤 이동하기 때문에 발생한 문제.
paint 되기 전, 스크롤을 이동하도록 하여 수정
  • Loading branch information
Creative-Lee authored Sep 14, 2023
1 parent 1314081 commit 118894a
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 20 deletions.
48 changes: 30 additions & 18 deletions frontend/src/features/youtube/components/Youtube.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable react/display-name */
import { useEffect } from 'react';
import { useCallback, useRef, useState } from 'react';
import { styled } from 'styled-components';
import createObserver from '@/shared/utils/createObserver';
import useVideoPlayerContext from '../hooks/useVideoPlayerContext';

interface YoutubeProps {
Expand All @@ -9,9 +10,11 @@ interface YoutubeProps {
}

const Youtube = ({ videoId, start = 0 }: YoutubeProps) => {
const { videoPlayer, initPlayer, bindUpdatePlayerStateEvent } = useVideoPlayerContext();
const { initPlayer, bindUpdatePlayerStateEvent } = useVideoPlayerContext();
const [loading, setLoading] = useState(true);
const observerRef = useRef<IntersectionObserver | null>();

useEffect(() => {
const createPlayerOnObserve: React.RefCallback<HTMLImageElement> = useCallback((domNode) => {
const createYoutubePlayer = async () => {
try {
new YT.Player(`yt-player-${videoId}`, {
Expand All @@ -23,6 +26,7 @@ const Youtube = ({ videoId, start = 0 }: YoutubeProps) => {
onReady: (e) => {
bindUpdatePlayerStateEvent(e);
initPlayer(e);
setLoading(false);
},
},
});
Expand All @@ -32,20 +36,24 @@ const Youtube = ({ videoId, start = 0 }: YoutubeProps) => {
}
};

createYoutubePlayer();
if (domNode !== null) {
observerRef.current = createObserver(createYoutubePlayer);
observerRef.current.observe(domNode);
return;
}

const clonePlayerRef = videoPlayer;

return () => {
if (!clonePlayerRef.current) return;

clonePlayerRef.current.destroy();
clonePlayerRef.current = null;
};
}, [bindUpdatePlayerStateEvent, initPlayer, start, videoId, videoPlayer]);
observerRef.current?.disconnect();
}, []);

return (
<YoutubeWrapper>
{loading && (
<Preview
src={`https://img.youtube.com/vi/${videoId}/hqdefault.jpg`}
ref={createPlayerOnObserve}
loading="lazy"
/>
)}
<YoutubeIframe id={`yt-player-${videoId}`} />
</YoutubeWrapper>
);
Expand All @@ -54,12 +62,16 @@ const Youtube = ({ videoId, start = 0 }: YoutubeProps) => {
export default Youtube;

export const YoutubeWrapper = styled.div`
aspect-ratio: auto 16 / 9;
position: relative;
aspect-ratio: 16 / 9;
width: 100%;
/* @media (max-width: ${({ theme }) => theme.breakPoints.xxs}) {
width: 90%;
} */
`;

export const YoutubeIframe = styled.div``;

const Preview = styled.img`
position: absolute;
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
`;
4 changes: 2 additions & 2 deletions frontend/src/pages/SongDetailListPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react';
import { useEffect, useLayoutEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { styled } from 'styled-components';
import SongDetailItem from '@/features/songs/components/SongDetailItem';
Expand Down Expand Up @@ -64,7 +64,7 @@ const SongDetailListPage = () => {
return () => nextObserver.disconnect();
}, [fetchExtraNextSongDetails, songDetailEntries]);

useEffect(() => {
useLayoutEffect(() => {
itemRef.current?.scrollIntoView({ behavior: 'instant', block: 'start' });
}, [songDetailEntries]);

Expand Down

0 comments on commit 118894a

Please sign in to comment.