정답: A 1, C 0, B 1이 순서대로 출력된다.
You clicked 0 times
Console
이전 렌더링의 클린업은 다음 렌더링의 컴포넌트가 브라우저에 그려진 이후에 실행된다.
각 렌더링마다 코드들이 어떤 순서대로 실행되는지 자세하게 알아보겠다.
-
첫 번째 렌더링
- 컴포넌트 내부의 코드가 실행된다. 즉,
count,setCount가 정의되고 콘솔에A 0이 출력된다. - 렌더링 결과물인
<p>You clicked 0 times</p>...가 화면에 그려진다. useEffect의 셋업이 실행된다. 즉, 콘솔에B 0이 출력된다.
- 컴포넌트 내부의 코드가 실행된다. 즉,
-
(버튼을 클릭하여) 두 번째 렌더링
- 컴포넌트 내부의 코드가 실행된다. 즉,
count,setCount가 정의되고 콘솔에A 1이 출력된다. - 렌더링 결과물인
<p>You clicked 1 times</p>...가 화면에 그려진다. useEffect의 클린업이 실행된다. 즉, 콘솔에C 0이 출력된다.useEffect의 셋업이 실행된다. 즉, 콘솔에B 1이 출력된다.
- 컴포넌트 내부의 코드가 실행된다. 즉,
-
(버튼을 한 번 더 클릭하여) 세 번째 렌더링
- 컴포넌트 내부의 코드가 실행된다. 즉,
count,setCount가 정의되고 콘솔에A 2이 출력된다. - 렌더링 결과물인
<p>You clicked 2 times</p>...가 화면에 그려진다. useEffect의 클린업이 실행된다. 즉, 콘솔에C 1이 출력된다.useEffect의 셋업이 실행된다. 즉, 콘솔에B 2이 출력된다.
- 컴포넌트 내부의 코드가 실행된다. 즉,
...
이처럼 리액트는 항상 페인팅 이후 useEffect를 다룬다. 이펙트가 스크린 업데이트를 가로막지 않기 때문에 앱을 더 빠르게 만들어준다.
function Input() {
const [text, setText] = useState("");
useEffect(() => {
console.log(text);
setText("default");
});
return (
<div>
{text}
<input
value={text}
onChange={(e) => {
setText(e.target.value);
}}
/>
</div>
);
}예를 들어 위와 같이 코드를 짜면 입력한 글자가 (아무 짧은 시간 동안이지만) 화면에 보인다는 것을 확인할 수 있다. 이 역시 리액트가 페인팅 이후에 useEffect를 처리하기 때문이다.
참고로 개발 환경에서 해당 컴포넌트의 동작은 다음과 같다.
- 맨 처음에 마운트되면 콘솔에
A 0,A 0,B 0,C 0,B 0을 출력한다. - 버튼을 한 번 누르면 콘솔에
A 1,A 1,C 0,B 1을 출력한다. - 버튼을 한 번 누르면 콘솔에
A 2,A 2,C 1,B 2을 출력한다. - ...
배포 환경과 다르게 개발 환경에서 더 많은 출력이 생기는 이유는 다음의 두 규칙을 통해 이해할 수 있다. Strict Mode가 활성화 되어있을 경우,
- 리액트는 컴포넌트가 순수 함수(pure function)인지 확인하기 쉽도록 하기 위해 기본적으로 렌더링을 2번씩 진행한다.
- 리액트는
useEffect의 셋업 및 클린업 로직이 잘 작성되었는지 확인하기 쉽도록 하기 위해 실제 셋업 이전에 한 번씩의 셋업과 클린업을 추가적으로 실행한다.
참고자료