[React] 이미지가 로드된 이후 렌더링

게코젤리·2023년 3월 15일
0
post-custom-banner

useQuery로 영화 정보를 가져오는데 불러올 이미지들이 많다보니 제각각 다른 속도로 화면에 나타나는 게 문제였다.

아.. 너무 고생했다.

변경 전 코드

function Search() {
  const offset = 5;
  const location = useLocation();
  const keyword = new URLSearchParams(location.search).get('keyword');

  const { data, isLoading, isError } = useQuery<IGetMovieResult>(
    ['search', keyword],
    () => GetSearched(keyword || '')
  );

  return (
    <Wrapper>
      <Row offset={offset}>
        {data?.results.map((movie) => (
          <Box
            offset={offset}
            key={movie.id}
            variants={boxVariants}
            initial="normal"
            whileHover="hover"
            transition={{ type: 'tween' }}
          >
            <img
              src={
                movie.poster_path
                  ? makeImagePath(movie.poster_path, 'w300')
                  : require('../assets/no-image-icon-6.png')
              }
              alt={movie.title}
            />
          </Box>
        ))}
      </Row>
    </Wrapper>
  );
}

변경 후 코드

function Search() {
  const offset = 5;
  const location = useLocation();
  const keyword = new URLSearchParams(location.search).get('keyword');

  const { data, isLoading, isError } = useQuery<IGetMovieResult>(
    ['search', keyword],
    () => GetSearched(keyword || '')
  );

  const [loadedImagesCount, setLoadedImagesCount] = useState(0);

  useEffect(() => {
    if (data) {
      setLoadedImagesCount(0);
      const images = data.results.map((movie) => {
        const img = new Image();
        img.src = movie.poster_path
          ? makeImagePath(movie.poster_path, 'w300')
          : require('../assets/no-image-icon-6.png');
        img.onload = () => {
          setLoadedImagesCount((prevCount) => prevCount + 1);
        };
        return img;
      });

      return () => {
        images.forEach((img) => img.onload = null);
      };
    }
  }, [data]);

  const isAllImagesLoaded = loadedImagesCount === data?.results.length;
  
  console.log(isAllImagesLoaded);
  return (
    <Wrapper>
      {isAllImagesLoaded && (
        <Row offset={offset}>
          {data?.results.map((movie) => (
            <Box
              offset={offset}
              key={movie.id}
              variants={boxVariants}
              initial="normal"
              whileHover="hover"
              transition={{ type: 'tween' }}
            >
              <img
                src={
                  movie.poster_path
                    ? makeImagePath(movie.poster_path, 'w300')
                    : require('../assets/no-image-icon-6.png')
                }
                alt={movie.title}
              />
              
            </Box>
          ))}
        </Row>
      )}
    </Wrapper>
  );
}

이미지 로드가 완료되었는지 추적할 수 있는 state를 만들고, 모든 이미지가 로드된 것을 확인한 후 렌더링 하도록 했다.

검색 결과를 가져오면 useEffect 훅에서 각 영화 포스터의 URL을 생성하고, 새 Image 인스턴스를 만들어 각 URL을 할당한 후, 이미지가 로드 될 때마다 useState 훅으로 loadedImagesCount를 증가시킨다. loadedImagesCount가 데이터의 결과 수와 일치하는 경우, 모든 이미지가 로드되었다는 뜻. 이때 isAllImagesLoaded를 true로 설정하고 이미지를 렌더링한다.

post-custom-banner

0개의 댓글