클래스 컴포넌트를 사용해본 경험이 있는 경우 LifeCycle
과 useEffect
의 차이를 확실히 알고 넘어가는 것이 좋다.
다음 세개의 메소드가 핵심이다.
1. 컴포넌트가 마운트될 때 : componentDidMount
2. 컴포넌트가 리렌더링될 때 : componentDidUpdate
3. 컴포넌트가 마운트 해제되기 전에 : componentWillUnmount
LifeCycle Method
는 생명주기 메서드라고 부른다. 생명주기 메서드는 컴포넌트가 브라우저상에 나타나고, 업데이트되고, 사라지게 될 때 호출되는 메서드이다. 추가적으로 컴포넌트에서 에러가 났을 때 호출되는 메서드도 있다.
클래스 컴포넌트의 라이프 사이클은 다음과 같다.
Component가 새롭게 생성되는 시점이다. Component 함수가 실행되고 결과물로 나온 Element들이 가상 DOM에 삽입되고 실제 DOM을 업데이트하기까지의 과정이다. 여기서 Mount
는 컴포넌트가 처음 실행된 걸 말한다.
this.props
와 this.state
값을 검증
하며, 반환값을 화면에 렌더링시킨다. render() 내부에서 this.state값을 변경하면 무한 업데이트가 일어날 수 있으므로, 순수함수로 작성되어야 한다.render() {
return (
<div>
<SomeComponent />
<OtherComponent />
</div>
)
}
(1) 이벤트 리스너 등록 및 ajax 처리
(2) setTimeout, setInterval 실행
componentDidMount에서 바로 setState를 실행할 경우, 렌더링이 완료되고 다시 업데이트하여 또 다시 렌더링을 하게 된다. 하지만 이 경우에는 사용자는 2번 실행되는 렌더링 과정을 볼 수 없다. 브라우저에 화면을 갱신하기 전 실행되기 때문이다.
하지만 이렇게 2번 렌더링 되는 과정은 성능상으로 문제를 일으킬 수 있으므로 주의가 필요하다. 필요한 경우를 제외하면 최초에 실행되는 생성자(constructor)에서 세팅해야 한다.
Component들은 state
나 props
가 변경이 되면 update가 진행이 되며 다시 rendering
된다.
component가 update될 때 아래의 순서대로 메소드가 실행이 된다.
1. New Props / setState()
2. render()
3. componentDidUpdate()
New Props / setState() : 상위 Component로부터 갱신된 props 를 받는 경우 / 현재 자신이 가진 state를 변경하는 경우
componentDidUpdate : componentDidUpdate
는 update 가 이루어지고 render가 완료된 직후 실행되는 메소드이다. 최초 마운트 될 때는 실행되지 않는다.
componentDidUpdate
를 이용할 때, setState
를 주의해야 한다. 조건문 등으로 제어해두지 않으면 무한 루프에 빠질 수 있기 때문이다.
setState > componentDidupdate > SetSate ...
해당하는 Component가 DOM상에서 제거
가 될 때 실행되는 lifeCycle이다.
주로 componentDidMount()에서 등록한 이벤트 리스너, setInterval, setTimeout을 클리어 하거나 외부 라이브러리 인스턴스 제거 등에 사용된다.
React Hook에서는 useEffect
를 통해 react클래스의 componentDidMount나 componentDidUpdate, componentWillUnmount와 같은 목적으로 lifeCycle
를 관리한다.
다음 사진으로 모든 것이 정리가 된다.
useEffect(() => {
}, [])
useEffect deps가 빈 배열의 경우 처음 Mount(첫 rendering)
될 때 호출된다.
componentDidMount()
와 비슷하다.
만약 useEffect
함수 안에 의존성 배열을 명시하지 않고 state를 변경하는 작업을 한다면 무한루프가 발생한다. 아래와 같이 말이다..
const Example = () => {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1); //무한루프 발생
});
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
);
};
이를 막기 위해서는 이렇게 두 번째 인자로 의존성 배열(deps)
을 명시해줘야 한다. 리액트는 컴포넌트가 변경된다고 하더라도 count가 달라졌나 확인하고 달라지지 않았다면 effect를 실행하지 않을 것이다. componentDidUpdate()
와 비슷하다.
const Example = () => {
const [count, setCount] = useState(0);
useEffect(() => {}, [count]); //count가 변경되었을때에만 useEffect 실행
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
);
};
const UseEffectExample = () => {
const [test, setTest] = useState('initial value');
useEffect(() => {
console.log('렌더링!');
});
return (
<div>
<p>{test}</p>
<input onChange={(e) => {setTest(e.target.value)}} />
</div>
)
}
다음 사진을 보면 해당 컴포넌트가 렌더링 될 때마다 useEffect함수가 실행되면서 콘솔에 로그를 남긴다. 다시 말하면 rendering 이후 componentDidUpdate() 가 계속 실행되는 것
과 비슷하다.
clean-up event
는 react가 DOM에 그리고 난 이후에 실행이 된다. 아래는 매초마다 count를 1씩 올려주는 코드의 예시이다. 쉽게 생각하면 useEffect를 뒷정리 해주는 거라고 생각하면 된다. 주로 setInterval이나 setTimeOut과 같은 함수들을 사용할 때 사용한다.
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);
return (
<>
<h1>{count}</h1>
</>
);
}
1. react 공식문서
2. react useEffect hook 번역 블로그
3. useEffect 관련 블로그
잘 읽었습니다. 감사합니다