[React] Lifecycle

뚜벅맨·2021년 8월 3일
0

컴포넌트 생명주기

모든 컴포넌트는 여러 종류의 Lifecycle, 즉 '생명 주기' 메서드를 가집니다. 이 메서드를 오버라이딩하면 특정 시점에 코드가 실행되도록 설정할 수 있습니다. 다음은 Lifecycle을 나타낸 도표입니다.

마운트

아래 메서드들은 컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출됩니다.

  • constructor()
  • static getDerivedStateFromProps()
  • **render()
  • componentDidMount()**

업데이트

props 또는 state가 변경되면 갱신이 발생합니다. 아래 메서드들은 컴포넌트가 재렌더링될 때 순서대로 호출됩니다.

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

마운트 해제

아래 메서드는 컴포넌트가 DOM 상에서 제거될 때에 호출됩니다.

  • componentWillUnmount()

오류 처리

아래 메서드들은 자식 컴포넌트를 렌더링하거나, 자식 컴포넌트가 생명주기 메서드를 호출하거나, 또는 자식 컴포넌트가 생성자 메서드를 호출하는 과정에서 오류가 발생했을 때에 호출됩니다.

  • static getDerivedStateFromError()
  • componentDidCatch()

자주 사용되는 생명주기 메서드

1. render()

render() 메서드는 클래스 컴포넌트에서 반드시 구현돼야하는 유일한 메서드입니다.

이 메서드가 호출되면 this.props와 this.state의 값을 활용하여 아래의 것 중 하나를 반환해야 합니다.

  • React 엘리먼트. 보통 JSX를 사용하여 생성됩니다. 예를 들어, <div /><MyComponent />는 React가 DOM 노드 또는 사용자가 정의한 컴포넌트를 만들도록 지시하는 React 엘리먼트입니다.
  • 배열과 Fragment. render()를 통하여 여러 개의 엘리먼트를 반환합니다.
  • Portal. 별도의 DOM 하위 트리에 자식 엘리먼트를 렌더링합니다.
  • 문자열과 숫자. 이 값들은 DOM 상에 텍스트 노드로서 렌더링됩니다.
  • Boolean 또는 null. 아무것도 렌더링하지 않습니다. (대부분의 경우 return test && <Child /> 패턴을 지원하는 데에 사용되며, 여기서 test는 boolean 값입니다.)

render() 컴포넌트의 state를 변경하지 않고 호출될 때마다 동일한 결과를 반환해야 하며, 브라우저와 직접적으로 상호작용을 하지 않습니다.

브라우저와 상호작용하는 작업이 필요하다면, 해당 작업을 componentDidMount()이나 다른 생명주기 메서드 내에서 수행하는 것이 좋습니다.

2. constructor()

사용에 앞서서 constructor(props) 메서드를 바인딩하거나 state를 초기화하는 작업이 없다면, 해당 React 컴포넌트에는 constructor를 구현하지 않아도 됩니다.

React 컴포넌트의 생성자는 해당 컴포넌트가 마운트되기 전에 호출됩니다. React.Component를 상속한 컴포넌트의 생성자를 구현할 때에는 다른 구문에 앞서 super(props)를 호출해야 합니다. 그렇지 않으면 this.props가 생성자 내에서 정의되지 않아 버그로 이어질 수 있습니다.


React에서 생성자는 보통 아래의 두 가지 목적을 위하여 사용됩니다:

  • this.state에 객체를 할당하여 지역 state를 초기화
  • 인스턴스에 이벤트 처리 메서드를 바인딩

⚠️ 주의사항 ⚠️
1) constructor() 내부에서 setState()를 호출하면 안 됩니다. 컴포넌트에 지역 state가 필요하다면 생성자 내에서 this.state에 초기 state 값을 할당하면 됩니다.

constructor(props) {
  super(props);
  // 여기서 this.setState()를 호출하면 안 됩니다!
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

생성자는 this.state를 직접 할당할 수 있는 유일한 곳입니다. 그 외의 메서드에서는 this.setState()를 사용해야 합니다.

2) state에 props를 복사하면 안 됩니다!

constructor(props) {
 super(props);
 // 이렇게 하지 마세요!
 this.state = { color: props.color };
}

이것은 불필요한 작업이며(this.props.color를 직접 사용하면 됩니다), 버그를 발생시킵니다(color props의 값이 변하더라도 state에 반영되지 않습니다).

3. componentDidMount()

componentDidMount()는 컴포넌트가 마운트된 직후, 즉 트리에 삽입된 직후에 호출됩니다. DOM 노드가 있어야 하는 초기화 작업은 이 메서드에서 이루어지면 됩니다. 외부에서 데이터를 불러와야 한다면, 네트워크 요청을 보내기 적절한 위치입니다.

componentDidMount()에서 즉시 setState()를 호출하는 경우도 있습니다. 이로 인하여 추가적인 렌더링이 발생하고 render()가 두 번 호출되지만, 브라우저가 화면을 갱신하기 전에 이루어지기 때문에 사용자는 그 중간 과정을 볼 수 없습니다. 대부분의 경우, 앞의 방식을 대신하여 constructor() 메서드에서 초기 state를 할당할 수 있습니다. 하지만 모달(Modal) 또는 툴팁과 같이 렌더링에 앞서 DOM 노드의 크기나 위치를 먼저 측정해야 하는 경우 이러한 방식이 필요할 수 있습니다.

4. componentDidUpdate()

componentDidUpdate()는 갱신이 일어난 직후에 호출됩니다. 이 메서드는 최초 렌더링에서는 호출되지 않습니다.

컴포넌트가 갱신되었을 때 DOM을 조작하기 위하여 이 메서드를 활용하면 좋습니다. 또한, 이전과 현재의 props를 비교하여 네트워크 요청을 보내는 작업도 이 메서드에서 이루어지면 됩니다

componentDidUpdate(prevProps) {
  // 전형적인 사용 사례 (props 비교를 잊지 마세요)
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

componentDidUpdate()에서 setState()를 즉시 호출할 수도 있지만, 위의 예시처럼 조건문으로 감싸지 않으면 무한 반복이 발생할 수 있습니다! 또한 추가적인 렌더링을 유발하여, 비록 사용자는 눈치채지 못할지라도 컴포넌트 성능에 영향을 미칠 수 있습니다.

5. componentWillUnmount()

componentWillUnmount()는 컴포넌트가 마운트 해제되어 제거되기 직전에 호출됩니다. 이 메서드 내에서 타이머 제거, 네트워크 요청 취소, componentDidMount() 내에서 생성된 구독 해제 등 필요한 모든 정리 작업을 수행하면 됩니다.

이제 컴포넌트는 다시 렌더링되지 않으므로, componentWillUnmount() 내에서 setState()를 호출하면 안 됩니다.

Lifecycle 기본 순서

  1. constructor
  2. render
  3. componentDidMount
  4. (fetch 완료)
  5. render
  6. (setState)
  7. componentDidUpdate (setState 되었기 때문에 컴포넌트 업데이트 발생)
  8. componentWillUnmount

부모 - 자식 Lifecycle 순서

  1. 부모 constructor
  2. 부모 render (부모 render 안에 있는 자식 컴포넌트로 넘어감)
  3. 자식 constructor
  4. 자식 render
  5. 자식 componentDidMount (여기까지 하고 부모 컴포넌트로 다시 넘어감)
  6. 부모 componentDidMount
  7. 부모 fetch 완료 (setState 발생 > 업데이트 발생 > 다시 render)
  8. 부모 render (부모 render 안에 있는 자식 컴포넌트로 넘어감)
  9. 자식 render
  10. 자식 componentDidUpdate
    (componentDidMount는 최초 렌더 시 한 번만 실행되기 때문에 componentDidUpdate 발생. 여기까지 하고 다시 부모로 넘어감.)
  11. 부모 componentDidUpdate
    (componentDidMount는 최초 렌더 시 한 번만 실행되기 때문에 componentDidUpdate 발생)

profile
쉽게만 살아가면 재미없어 빙고🐝

0개의 댓글