리액트 라이프 사이클과 useEffect Hook
모든 리액트 컴포넌트는 '생명주기 메서드'를 가지며 이를 오버라이딩 하여 특정 시점에 코드를 실행되도록 할 수 있다.
이 글에서 작성하는 것 보다 많은 라이프 사이클 관련 메서드가 존재하지만 위 도표에 나오는 일반적인 라이프사이클 메소드만 기술하였습니다.
마운트
컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입 될 때 아래 메서드들이 순서대로 호출된다.
constructor()
- 해당 컴포넌트가 마운트 되기 전에 호출되는 React 컴포넌트 생성자.
- 메서드를 바인딩하거나 state를 초기화 하는 작업이 없다면 해당 React컴포넌트에서는 구현하지 않아도 된다.
- 보통 state를 초기화 하거나 인스턴스에 이벤트 처리 메서드를 바인딩 할 때 주로 사용한다.
render()
- 클래스 컴포넌트에서 반드시 구현되어야 하는 유일한 메서드.
- 이 메서드가 호출되면
this.props
와this.state
의 값을 활용하여 리액트 엘리면트를 반환한다(외에 배열과 Fragment, Portal, 문자열과 숫자, Boolean 또는 null 을 반환할 수도 있다 ) render()
함수는 순수해야 한다. 컴포넌트의 state가 변하지 않으면 호출 될 때 마다 동일한 결과를 반환해야 하며, 브라우저와 직접 상호작용을 하지 않는다.- 브라우저와 상호작용하는 작업이 필요하다면 아래에 기술될
componentDidMount()
나 다른 생명주기 메서드 내에서 수행해야 한다.
componentDidMount()
- 컴포넌트가 마운트 된 직후(트리에 삽입된 직후) 호출된다.
- DOM 노드가 있어야 하는 초기화 작업은 이 메서드 안에서 수행하면 된다.
- 외부에서 데이터를 불러와야 한다면 네트워크 통신을 하기에 적절한 위치이다.
- 데이터를 구독하기 좋은 위치.
componentDidUpdate()
에서 즉시 setState()를 호출하는 경우 추가 렌더링이 발생하지만 브라우저가 화면을 갱신하기 이전에 이루어진다.render()
함수는 두번 호출되지만, 사용자는 그 중간과정을 볼 수 없다.
그러나 이런 사용 방식은 성능 문제로 이어질수 있으므로 주의가 필요하다.
업데이트
props 혹은 state가 변경되면 컴포넌트가 다시 렌더링 되고, 이 때 아래 메서드들이 순서대로 호출 된다.
render()
- 위의 render()함수 설명 참고componentDidUpdate()
- 갱신이 일어난 직후에 호출된다.
- 최초 렌더링에서는 호출되지 않는다.
- 컴포넌트가 갱신되었을 때 DOM을 조작하기 위해 활용하기 좋은 메서드.
- 이전과 현재의props를 비교하여 작업을 처리할 수도 있다.
마운트 해제
컴포넌트가 DOM 상에서 제가될 때 호출된다.
componentWillUnmount()
- 마운트가 해제되어 제거되기 직전에 호출된다.
- 타이머제거, 네트워크 요청취소 ,
componentDidMound()
내에서 생성된 구독 해제 등 필요한 작업을 처리하는 메서드. - 이 메서드 이후엔 컴포넌트가 다시 렌더링되지 않으므로 이 메서드 안에서 setState()를 호출하면 안된다.
함수형 컴포넌트에서 라이프사이클 사용
라이프사이클 메서드는 리액트 클래스 컴포넌트 내에서 사용이 가능하므로 함수형 컴포넌트에서는 사용할 수 없다.
대신 사이드 이펙트를 추가하고 싶다면 useEffect()
훅을 사용할 수 있다.
리액트 공식 홈페이지에서는 useEffect()
훅과 관련하여 다음과 같이 설명한다.
React의 class 생명주기 메서드에 친숙하다면, useEffect Hook을 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각해도 좋습니다.
따라서 라이프사이클에 따른 side effects를 함수형 컴포넌트에서도 useEffect()
를 사용하여 처리할 수 있다.
useEffect
useEffect가 하는 일
- React 컴포넌트가 렌더링 이후에 어떤 일을 수행해야 하는지 설정 가능
- 우리가 넘긴 effect(함수)를 기억 했다가 DOM 업데이트를 수행한 이후에 불러낸다.
useEffect를 컴포넌트 안에서 불러내는 이유
- 컴포넌트 안에
useEffect
를 둠으로써 effect를 통해 state 혹은 props에 접근이 가능하다.
useEffect는 렌더링 이후 매번 수행되나?
- 기본적으로 렌더링이 될 때 마다 매번 수행된다. (최초에도 실행, 업데이트 되어도 실행됨)
- (클래스 컴포넌트의)마운팅과 업데이트라는 방식 대신 effect를 렌더링 이후이 발생하는 것으로 생각 할 수 있다)
- React는 effect가 수행되는 시점에 이미 DOM이 업데이트 되었음을 보장한다.
정리(clean-up)을 이용하는 Effects
- 외부 데이터를 구독(subscription)을 설정해야 하는 경우 메모리 누수가 일어나지 않게도록 사용하지 않는다면 정리(clean-up)하는 것이 중요하다.
- 클래스 컴포넌트에서는
componentDidMount
에서 구독을 설정한 뒤componentWillUnmount
에서 이를 정리(clean-up)한다.
useEffect에서 정리하기
clean-up을 위한 별개의 effect가 필요하지 않다. effect가 함수를 리턴하면 React는 그 함수를 정리해야 할 때 그것을 실행시킨다.
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// effect 이후에 어떻게 정리(clean-up)할 것인지 표시합니다.
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
effect에서 반드시 named function을 리턴해야 하는 것은 아니다.
화살표 함수나 다른 이름의 함수를 붙여도 무방하다.
구독의 추가와 제거가 모두 하나의 effect안에서 구성될 수 있다.
React에서 effect를 정리(clean-up)하는 시점
- 컴포넌트가 마운트 해제 될 때 clean-up된다.
- 그런데 위의 코드 예시를 보면 effect는 리렌더링이 일어날 때 마다 실행하게 된다.
- React가 effect를 실행하기 전에 이전의 렌더링에서 파생된 effect를 정리하는 이유가 이때문.
Effect의 성능 최적화
class 컴포넌트에서는 이전의 props나 state를 비교해서 변하지 않은 경우는 동작을 실행 하지 않을 수도 있다.
이런 동작을 useEffect
Hook 안에서 하기 위해서는 useEffect
의 두번째 인수로 배열을 넘겨주면 된다.
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // count가 바뀔 때만 effect를 재실행합니다.
위 예시에서 [count]
를 두번째 인수로 넘긴다.
리렌더링 이후 count
의 값이 이전과 변함이 없다면 effect를 실행하지 않는다.count
가 업데이트 되어서 리렌딩했한 후 비교값이 다르게 나오면 effect를 재실행 한다.
배열 내의 여러 개의 값이 있다면, 그 중 단 하나만 다를지라도 effect를 재실행한다.
이 최적화 방법을 사용한다면 배열이 컴포넌트 내에서 바뀌는 값들과 effect에 의해 사용되는 값들을 모두 포함해야 한다. 그렇지 않으면 이전의 렌더링 값을 참조하게 된다.
effect를 실행하고 이를 정리 하는 과정을 딱 한번만 실행하고 싶다면 빈배열'[]'을 두번째 인수로 넘기면 된다.
이렇게 하면 prop이나 state의 그 어떤 값에도 의존하지 않으며 재실행 되지 않는다.