Controller 컴포넌트에 있는 버튼을 클릭하면, Viewer 컴포넌트에 있는 카운트가 증가하거나 감소해야 한다.
이 기능을 구현하기 위해 버튼 클릭 이벤트가 발생했을 때 컴포넌트 값을 동적으로 렌더링시키는 State를 사용한다.
이 과정을 도해화하면 위와 같다. 카운트를 관리할 State를 만들고 초깃값을 0으로 설정한 후, 버튼을 클릭하면 현재 State 값을 버튼이 전달하는 값과 계산하여 변경한다. 그 후 변경된 State 값을 Viewer 컴포넌트에 전달하여 페이지의 카운트 값을 업데이트한다.
3-(2). State는 어떤 컴포넌트에 만들까?
오답 1: Viewer 컴포넌트Viewer 컴포넌트와 Controller 컴포넌트는 부모-자식 관계가 아니므로 Props를 사용할 수 없어 setCount를 전달할 방법이 없다.
오답 2: Controller 컴포넌트버튼을 클릭하면 State는 기존 값에서 해당 버튼의 숫자와 계산한 값으로 변경된다. Controller에 State를 생성할 경우, Viewer 컴포넌트와 Controller컴포넌트는 부모-자식 관계가 아니므로변경된 State 값을 Viewer 컴포넌트에 전달할 방법이 없다.
정답: App 컴포넌트
App.js 파일을 다음과 같이 수정한다.
import "./App.css";
import { useState } from "react";
import Controller from "./component/Controller";
import Viewer from "./component/Viewer";
function App() {
const [count, setCount] = useState(0);
const handleSetCount = (value) => {
setCount(count + value);
};
return (
<div className="App">
<h1>Simple Counter</h1>
<section>
<Viewer count={count} /> ① Viewer 컴포넌트에 State 변수 count의 값을 Props로 전달
</section>
<section>
<Controller handleSetCount={handleSetCount} /> ② Controller 컴포넌트에 State 값을 변경하는 함수 setCount를 Props로 전달
</section>
</div>
);
}
export default App;
다음으로 Viewer.js를 수정하여 Viewer 컴포넌트에서 App에서 받은 Props를 페이지에 렌더링한다.
위 코드의 첫번째 useEffect 함수는 코드 내에서 조건문을 사용해 제어하여 업데이트 시에만 동작하고, 두번째useEffect 함수는 의존성 배열에 빈 배열을 사용해 마운트 시에만 동작한다.
2-(5). 컴포넌트 언마운트 제어하기
프로그래밍에서 특정 함수가 실행되고 종료된 후에, 미처 정리하지 못한 사항을 처리하는 일을 클린업(Cleanup)이라 한다.
(...)
function App() {
(...)
useEffect(() => { ①
setInterval(() => { ②
console.log("깜빡");
}, 1000);
});
(...)
}
export default App;
저장한 직후에는 개발자 도구의 콘솔을 확인하면 1초마다 문자열 깜빡이 출력되지만, App 컴포넌트를 여러 번 리렌더 한 후에는 함수 setInterval에서 정한 인터벌(1초)이 아닌 매우 빠른 속도로 ‘깜빡’ 문자열이 콘솔에 출력되는 현상을 볼 수 있다.
이는 2가지 이유 때문인데,
App 컴포넌트를 렌더링할 때마다 useEffect의 콜백 함수는 새로운 setInterval 함수를 만들고 새 인터벌 간격을 생성.
함수 setInterval에서 인터벌을 생성한 다음에 이를 종료하지않음.
(...)
function App() {
(...)
useEffect(() => {
const intervalID = setInterval(() => { ① 함수 setInterval은 새 인터벌을 생성하면 인터벌 식별자(id)를 반환합니다. 이 id를 변수 intervalID에 저장.
console.log("깜빡");
}, 1000);
return () => { ② useEffect에 인수로 전달한 콜백 함수가 새 함수를 반환. (클린업 함수)
console.log("클린업");
clearInterval(intervalID); ③ 클린업 함수는 clearInterval을 호출.
};
});
(...)
}
export default App;
useEffect의 콜백 함수가 반환하는 함수를 클린업 함수라고 한다. 이 함수는 useEffect의 콜백 함수가 실행되기 전이나 컴포넌트가 언마운트하는 시점에 실행된다. 따라서 위 코드는 컴포넌트를 렌더링할 때 마다 새 인터벌을 생성하고 함수 clearInterval을 사용해 기존 인터벌은 삭제한다.
Chrome Web Store에서 [React Developer Tools] 검색 후 설치
아이콘이 잘 나타난다면 개발자 도구를 열고, 탭 메뉴에서 >> 모양의 더 보기 아이콘을 클릭하여 메뉴에서 [Components]와 [Profiler] 탭이 추가 되었는지 확인한다.이 두 탭에 컴포넌트 계층 구조의 확인이나 성능 측정 등 개발에 필요한 유용한 기능들이 있다.
[Components] 탭: 현재 리액트 앱의 컴포넌트 트리와 각 컴포넌트가 관리하는 State 등의 정보를 확인
[Profiler] 탭: 리액트 컴포넌트의 렌더링 성능을 측정
3-(2). 리액트 개발자 도구의 기능 사용하기
컴포넌트 트리 살펴보기 개발자 도구의 [Component] 탭을 클릭하여 현재 렌더링된 카운터 앱의 컴포넌트 트리를 시각적으로 확인할 수 있다.
State 모니터링하기 [Components] 탭에서 실시간으로 변하는 State 값을 잘 모니터링할 수 있다.
Props 모니터링하기 [Components] 탭에서 자식 컴포넌트가 부모 컴포넌트로부터 받는 Props 값을 확인할 수 있다.
리렌더 하이라이트 기능 사용하기 리렌더가 발생한 컴포넌트를 하이라이트하는 기능을 이용하면 어떤 컴포넌트가 의미 없이 리렌더되는지 알 수 있어 향후 렌더링 최적화에 도움이 된다.
[Components] 탭의 톱니 모양의 View Settings 아이콘 클릭.
대화상자 [General] 탭에서 "Highlight updates when component render" 항목을 체크.
리렌더 하이라이트 기능은 [Components] 탭이 열려 있어야만 확인 가능하다. 컴포넌트 영역이 노란색일수록 빠른 시간 내에 많은 리렌더가 발생했다는 의미이다.