개요
프로젝트 진행 중 메인 화면에서 지정된 시간마다 변경된 이미지를 보여줘야했다.
코드
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의 자세한 내용은 아래쪽 참고 사이트에서 확인하면 된다.
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초마다 배경 이미지가 변경되게 설정한 후 테스트한 화면이다.
참고 사이트
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
'🖥️Frontend > React' 카테고리의 다른 글
[React] 검색어와 동일한 단어 강조 (0) | 2024.07.30 |
---|---|
[React + TypeScript] Kakao Map API 적용해보기 (0) | 2024.07.03 |
[React] Vite를 사용해서 React 프로젝트 생성 및 실행 (0) | 2024.02.07 |
[React] canvas & x, y좌표 값으로 길 그리기 (0) | 2023.11.28 |
[React] RTK & TypeScript (1) | 2023.11.20 |
댓글