🖥️Frontend/React

[React] 지정한 시간 마다 백그라운드 이미지 변경하기 (with TypeScript)

뉴발자 2024. 3. 29.
728x90

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그림 1. React

 

 

개요

프로젝트 진행 중 메인 화면에서 지정된 시간마다 변경된 이미지를 보여줘야했다.

 

 

코드

Main.tsx

const Layout = styled.div<{ url: string }>`
  width: 100%;
  height: 100%;
  background: url(${(props) => props.url});
  background-size: cover;
  background-position: center center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: rgba(0, 0, 0, 0.7);
`;

const Main = () => {
  const [ activeIndex, setActiveIndex ] = useState(0);

  const nextSlice = useCallback(() => {
    if(activeIndex < images.length-1) setActiveIndex(activeIndex + 1);
    else setActiveIndex(0);
  }, [activeIndex]);

  // 10000ms(10s) 마다 화면을 변경한다.
  useEffect(() => {
      const interval = setInterval(() => {
      nextSlice()
    }, 1000 * 10);
    
    return () => {
      clearInterval(interval);
    }
  }, 1000 * 10);

  return (
    <Layout
      url={images[activeIndex]}
    ></Layout>
  );
};

export default Main;

 

하지만 위 코드 동작 시 activeIndex의 값이 1로 바뀐 후 더 이상 변화가 일어나지 않는다.

 

이는 외부 함수(setInterval)가 종료되도 내부 함수(setActiveIndex)는 그 값을 기억하기 때문에 일어나는 현상이다.

 

즉, 외부 함수인 setInterval는 종료됐지만 내부 함수인 setActiveIndex는 초기값인 0을 기억한다.

 

그래서 외부 함수가 종료되고 +1을 하더라도 그 activeIndex 값은 계속 1을 반환한다.

 

해결 방법은 2가지가 있는데 첫 번째는 setState에 callback을 넘겨주는 방법이다.

 

하지만 이 방법은 react의 lifecycle에 다소 벗어난 동작을 하게된다.

 

따라서 두 번째 방법인 Dan 이라는 사람이 만든 useInterval hook을 사용해서 구현했다.

 

setInterval과 useInterval의 자세한 내용은 아래쪽 참고 사이트에서 확인하면 된다.

728x90

 

 

useInterval hook 적용 코드

useInterval.tsx

 
import { useEffect, useRef } from 'react'
import { useIsomorphicLayoutEffect } from 'usehooks-ts'

const useInterval = (callback: () => void, delay: number | null) => {
  const savedCallback = useRef(callback)

  // Remember the latest callback if it changes.
  useIsomorphicLayoutEffect(() => {
    savedCallback.current = callback
  }, [callback])

  // Set up the interval.
  useEffect(() => {
    // Don't schedule if no delay is specified.
    // Note: 0 is a valid value for delay.
    if (delay === null) {
      return
    }

    const id = setInterval(() => {
      savedCallback.current()
    }, delay)

    return () => {
      clearInterval(id)
    }
  }, [delay])
};

export default useInterval;

 

Main.tsx

const Layout = styled.div<{ url: string }>`
  width: 100%;
  height: 100%;
  background: url(${(props) => props.url});
  background-size: cover;
  background-position: center center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: rgba(0, 0, 0, 0.7);
`;

const Main = () => {
  const [ activeIndex, setActiveIndex ] = useState(0);

  const nextSlice = useCallback(() => {
    if(activeIndex < images.length-1) setActiveIndex(activeIndex + 1);
    else setActiveIndex(0);
  }, [activeIndex]);

  // 10000ms(10s) 마다 화면을 변경한다.
  useInterval(() => {
    nextSlice();
  }, 1000 * 10);
  
  return (
    <Layout
      url={images[activeIndex]}
    ></Layout>
  );
};

export default Main;

 

 

실행

2초마다 배경 이미지가 변경되게 설정한 후 테스트한 화면이다.

그림 2. 2초마다 변경되는 배경화면

 

 

참고 사이트

https://mingule.tistory.com/65

 

React에서 setInterval 현명하게 사용하기(feat. useInterval)

들어가기 Babble의 방 목록 페이지에 들어가면 유저가 생성한 방들이 쭉 나열되어 있는 것을 볼 수 있다. (안타깝게도 유저가 없으면 방도 없다ㅜㅜ) 그리고 이 방들은 5초마다 서버에 요청을 보내

mingule.tistory.com

 

https://usehooks-ts.com/react-hook/use-interval

 

useInterval

Custom hook that creates an interval that invokes a callback function at a specified delay using the setInterval API.

usehooks-ts.com

 

 

 

 

 

 

 

 

 

 

728x90

댓글