[React] useEffect 무한 루프 해결하기, cleanup function
며칠 전, React에서 side effect가 무엇인지 찾아보다 좋은 글을 발견해 번역해서 포스팅하였다.
전체를 번역해 올리려다 내용이 생각보다 길어 두 개로 나눠서 업로드한다.
앞선 포스팅에서는 useEffect에서 side effect가 무엇인지, useEffect를 어떻게 사용하는지에 대해 알아보았다.
4. useEffect를 사용하며 발생하는 흔한 실수를 고치는 법
useEffect의 실수를 피하기 위해 알아야 할 몇 가지 감지하기 힘든 세부 사항이 있다.
useEffect에 종속성 배열을 전혀 제공하지 않고 함수만 제공하면 함수는 컴포넌트가 렌더링 될 때마다 렌더링 이후 실행된다.
이는 useEffect hook 내에서 state를 업데이트하려고 할 때 문제를 일으킬 수 있다.
useEffect에서 state를 업데이트하는 경우, React의 기본 동작은 컴포넌트를 다시 렌더링하는 것이다. 따라서 useEffect는 종속성 배열이 없을 때 모든 단일 렌더링 후에 실행되므로 무한 루프가 발생합니다.
// BEFORE
function MyComponent() {
const [data, setData] = useState([])
useEffect(() => {
fetchData().then(myData => setData(myData))
// Error! useEffect runs after every render without the dependencies array, causing infinite loop
});
}
첫 번째 렌더링 후 useEffect가 실행되고, state가 업데이트되어 다시 렌더링되어 useEffect가 다시 실행되어 프로세스가 무한대로 다시 시작된다.
이것을 무한 루프(infinite loop)라고 하며 이는 애플리케이션을 중단시킨다.
useEffect 내에서 state를 업데이트하는 경우 빈 종속성 배열을 제공해야 한다. useEffect에 빈 배열을 제공하면 컴포넌트가 처음 렌더링된 후 콜백 함수가 한 번만 실행된다.
이에 대한 일반적인 예는 데이터를 가져오는 것이다. 컴포넌트의 경우 데이터를 한 번만 가져와서 상태에 넣은 다음 JSX에 표시할 수 있습니다.
// AFTER
function MyComponent() {
const [data, setData] = useState([])
useEffect(() => {
fetchData().then(myData => setData(myData))
// Correct! Runs once after render with empty array
}, []);
return <ul>{data.map(item => <li key={item}>{item}</li>)}</ul>
}
5. useEffect의 cleanup function이란 무엇인가?
React에서 side effect를 제대로 수행하는 마지막 부분은 effect cleanup function이다.
때로는 side effect를 멈춰야 한다. 예를 들어 setInterval 함수를 사용하는 카운트다운 타이머가 있는 경우 clearInterval 함수를 사용하지 않으면 해당 interval은 멈추지 않는다.
또 다른 예는 WebSocket과 함께 구독을 사용하는 것이다. 구독은 더 이상 사용하지 않을 때 "꺼야" 하며, 이것이 바로 cleanup function이 존재하는 이유이다.
setInterval을 사용하여 state를 설정하고 그 side effect를 cleanup 하지 않으면, 컴포넌트가 언마운트되고 더 이상 사용하지 않을 때 state는 컴포넌트와 함께 소멸되지만 setInterval 함수는 계속 실행된다.
function Timer() {
const [time, setTime] = useState(0);
useEffect(() => {
setInterval(() => setTime(1), 1000);
// counts up 1 every second
// we need to stop using setInterval when component unmounts
}, []);
}
컴포넌트가 사라지는 경우 문제는 setInterval이 더 이상 존재하지 않는 state를 업데이트하려고 시도한다는 것이다. 이것은 메모리 누수(memory leak)라는 오류이다.
cleanup function을 사용하려면 useEffect 함수 내에서 함수를 반환해야 한다.
이 함수 내에서 cleanup을 수행할 수 있다. 이 경우에는 clearInterval을 사용해 setInterval을 중지한다.
function Timer() {
const [time, setTime] = useState(0);
useEffect(() => {
let interval = setInterval(() => setTime(1), 1000);
return () => {
// setInterval cleared when component unmounts
clearInterval(interval);
}
}, []);
}
컴포넌트가 언마운트될 때 cleanup function이 호출된다.
언마운트되는 컴포넌트의 일반적인 예는, 컴포넌트가 더 이상 렌더링되지 않는 애플리케이션의 새 페이지 또는 새 경로로 이동하는 것이다.
구성 요소가 언마운트되면 cleanup function이 실행되고 interval이 지워지며 존재하지 않는 state 변수를 업데이트하려고 시도하는 오류가 더 이상 발생하지 않는다.
마지막으로, cleanup이 모든 경우에 필요한 것은 아니다. 구성 요소가 언마운트될 때 반복되는 side effect를 중지해야 하는 경우와 같이 몇 가지 경우에만 필요하다.
🌟 내용에 오류가 있다면 댓글 달아주시면 감사하겠습니다.
References
https://www.freecodecamp.org/news/react-useeffect-absolute-beginners/
'프로그래밍 > React' 카테고리의 다른 글
[React] 파일과 데이터를 모두 body에 넣어 API 요청하기 | form 태그 활용기 (3) (0) | 2022.09.02 |
---|---|
[React] react-query 적용하기 | useQuery, refetch (0) | 2022.06.22 |
[React] Side effect(사이드 이펙트)란? | 부수 효과, useEffect (0) | 2022.06.03 |
[React] 여러 개의 input의 onChange 한 번에 관리하기 (0) | 2022.05.22 |
[React] useNavigate 슬래시 유무에 따른 차이 (React Router v6) (0) | 2022.05.09 |