useEffect의 cleanUp 함수에 대한 착각 : unmount시 발생하는 것이 아니다!
들어가기에 앞서 결론부터 말하자면 공식문서에 나와있다
실제 실험을 통해 페이지 이동(unmount)이 안되어도 setInterval을 정리하여 버그를 차단함을 알 수 있었다.
1. clean-up 없을 때
import { useEffect, useState } from "react";
const initialCount = 0;
function FrequentlyChangableDependencyInEffect() {
const [count, setCount] = useState<number>(initialCount);
useEffect(() => {
setInterval(() => {
setCount(count);
}, 1000);
}, [count]);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</>
);
}
export default FrequentlyChangableDependencyInEffect;
state가 꼬여버린다.
2. clean-up 있을 때
(+ 공식문서 참고하여 functional Update 로 변형함 )
import { useEffect, useState } from "react";
const initialCount = 0;
function FrequentlyChangableDependencyInEffect() {
const [count, setCount] = useState<number>(initialCount);
useEffect(() => {
const id = setInterval(() => {
setCount((c) => c + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</>
);
}
export default FrequentlyChangableDependencyInEffect;
이는 페이지 전환이 되지 않아도 매 업데이트시마다 clean-up이 실행되어 setInterval을 정리해주는 모습이다.
이제 설명으로 들어가자!
Clean-up 함수란?
useEffect의 clean-up함수는 쉽게 생각하면 뒷정리를 해주는 함수이다.
공식문서에 따르면
다음 3가지 경우는 clean-up함수가 필요 없는 경우이다.
- DOM 수동조작
- 네트워크 요청
- 로깅
실행 이후 신경쓸 것이 없기 때문
clean-up함수가 필요한 경우는 다음과 같다.
- setInterval, setTimeout 을 사용하여 등록한 작업들 clear 하기 (clearInterval, clearTimeout)
- 라이브러리 인스턴스 제거
페이지 전환시에 남아있으면 안되는 기능들을 정리할 때 쓰인다. (예시 : setTimeout때문에 메모리 누수가 생기는 상황)
useEffect(()=>{
let mounted = true;
setTimeout(()=> {
if(mounted){
return NextBtn();
}
},5000)
return () => {mounted = false;};
});
// 페이지 전환시 setTimeout이 실행되지 않도록 함
그럼 clean-up함수는 언마운트시 1회 실행되는 것인가?
공식문서를 보면 그렇지 않다.
우선 결론부터 말하면, class의 생명주기로 굳이 치환했을 때 componentWillUnmount가 아니라 componentDidUpdate였다.
(블로그들에서 clean-up함수가 unmount될 때 1번 실행된다는 글들이 있는데 다 잘못알고 하는 말인 듯 하다. 물론 나도 그렇게 썼고)
컴포넌트가 화면에 표시되어 있는 동안 friend prop이 변한다면 컴포넌트는 다른 friend의 상태에 영향이 갈 수 있다.
또 마운트 해제가 일어나는 동안 구독 해지 호출이 다른 friend ID를 사용하여 메모리 누수나 충돌이 발생할 수 있다.
클래스형 리액트에서는 이런 경우들을 다루기 위해 componentDidUpdate를 사용한다.
props 변화 처리가 완료되었을 때 이전 friend를 unsubscribe시키고 다음friend를 subscribe 시키는 것이다.
이를 함수형 컴포넌트에서 표시하면 다음과 같다!
위와 같은 경우는 언마운트시 실행된 것이 아니라, friend가 바뀌었을때
이전 friend를 unsubscribe시키고 현재 friend를 subscribe시킨 것이다.
위 함수형 예시 코드를 실행순서대로 풀면 subscribe와 unsubscribe를 반복하는 다음 코드가 된다.
다시한번 말하지만, unsubscribe시키는 clean-up함수는 unmount시 실행된 것이 아니라, friend가 바뀌었을 때 실행되는 것이다.
매 랜더링마다 다음의 effect를 적용하기 전에 이전의 effect를 정리(clean-up)하는 것이다.(의존성 배열이 있다면 의존성 배열의 값이 변할때마다)
참고
리액트 공식문서
'개발 > React' 카테고리의 다른 글
React.memo - Memoization을 이용한 불필요 리렌더링 없애기 (0) | 2022.09.18 |
---|---|
Functional update of setState -> state의 변화가 state를 참조하지 않게 됨 (0) | 2022.08.13 |
Flux 패턴 (0) | 2022.08.11 |
Suspense (0) | 2022.08.10 |
lodash 이용해서 프론트에서 고유한 id 만들기 (0) | 2022.07.31 |