본문으로 바로가기

 

라이프사이클이란?


  • 리액트 컴포넌트는 생명을 가지고 있는데, 이 생명의 주기는 페이지에 렌더링되기 전인 준비 과정에서 시작하여 페이지에서 사라질 때 끝난다
  • 이러한 수명(생명 주기)을 간단하게 3부분으로 나누어서 각각 파트에서 불러오는 메서드들을 라이프사이클 메서드라고 부른다
  • 라이프사이클 메서드는 클래스형 컴포넌트에서만 사용이 가능하다

7-1. 라이프사이클 메서드의 이해


라이프사이클 메서드의 종류는 총 9가지이다

  • Will 접두사가 붙은 메서드 : 작업하기 전에 실행되는 메서드
  • Did 접두사가 붙은 메서드 : 작업을 한 후에 실행되는 메서드

그리고 위에서 말했듯이 수명을 3가지 부분으로 나누는데

  • 마운트 : DOM이 생성되고 웹 브라우저상에 나타나는 것
  • 업데이트 : 컴포넌트의 정보가 바뀌어서 리렌더링될 때
  • 언마운트 : DOM에서 컴포넌트를 제거하는 것

7-1-1. 마운트


마운트 단계에서 호출하는 메서드는 다음과 같다

  • constructor : 컴포넌트를 새로 만들 때마다 호출되는 생성자 메서드
  • getDerivedStateFromProps : props에 있는 값을 state와 동기화시킬 때 호출하는 메서드
  • render : 우리가 JSX로 작성한 View를 렌더링하는 메서드
  • componentDidMount : 컴포넌트가 웹 브라우저상에 나타난 후 호출되는 메서드

7-1-2. 업데이트


컴포넌트는 다음과 같은 경우에 업데이트한다

  1. props나 state가 바뀔 때
  2. 부모 컴포넌트가 리렌더링될 때
  3. this.forceUpdate로 강제로 렌더링을 트리거할 때

이렇게 업데이트되면 리렌더링되면서 다음과 같은 메서드들을 호출한다

  • getDerivedStateFromProps : props값의 변화에 따라 state의 값에 변화를 줄 때 사용하는 메서드
  • shouldComponentUpdate : 이 메서드에서 true를 반환하면 다음 단계로 진행, false를 반환하면 작업을 중지시켜 리렌더링을 멈춘다 다른 함수에서 this.forceUpdate를 호출하면 이 단계는 생략된다
  • render : 컴포넌트를 리렌더링한다
  • getSnapShotBeforeUpdate : 컴포넌트의 변화를 DOM에 반영하기 바로 직전에 호출하는 메서드
  • componentDidUpdate : 컴포넌트의 업데이트 작업이 끝난 후 호출하는 메서드

7-1-3. 언마운트


  • componentWillUnmount : 웹 브라우저상에서 사라지기 전에 호출하는 메서드

7-2. 메서드 살펴보기


위에서 나열한 메서드 중 몇가지만 살펴보자

7-2-1. getDerivedStateFromProps 메서드


static getDerivedStateFromProps(nextProps, prevState){
    // 조건에 맞추어서 값을 동기화
    if(nextProps.value !== prevState.value){
        return {value : nextProps.value}; 
    }
    // 변경하지 않으면 null
    return null; 
}

7-2-2. getSnapshotBeforeUpdate 메서드


이 메서드는 render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출되며 반환된 값은 후술할 componentDidUpdate에서 3번째 파라미터인 snapshot값으로 전달 받을 수 있다

getSnapshotBeforeUpdate(prevProps, prevState){
    if(prevState.array !== this.state.array){
        const {scrollTop, scrollHeight} = this.list;
        return {scrollTop, scrollHeight};
    }
}

7-2-3. componentDidUpdate 메서드


getSnapshotBeforeUpdate(preProps, preState, snapShot){}

업데이트가 끝난 직후이므로 DOM 관련 처리를 해도 무방하다

prevProps, prevState를 이용하여 컴포넌트가 가졌던 데이터에 접근할 수 있으며 getSnapshotBeforeUpdate에서 반환한 값을 snapshot으로 전달받을 수 있다

7-2-4. componentDidCatch 메서드


컴포넌트 렌더링 도중에 에러가 발생했을 경우 먹통을 방지하고 오류 UI를 볼 수 있게 해준다

componentDidCatch(error, info){
    this.setState({
        error : true,    
    });
    console.log({error, info});
}

error는 어떤 오류가 발생했는지 info는 어느 코드에서 발생했는지에 대한 정보를 알려준다

7-3. 라이프사이클 메서드 사용하기


import React, { Component } from 'react';

class LifeCycleSample extends Component {
    state = {
        number: 0,
        color: null
    };
    myRef = null; // ref를 설정할 부분
    constructor(props) {
        super(props);
        console.log('constructor');
    }
    static getDerivedStateFromProps(nextProps, prevState) {
        console.log('getDerivedStateFromProps');
        if (nextProps.color !== prevState.color) {
            return { color: nextProps.color };
        }
        return null;
    }
    componentDidMount() {
        console.log('componentDidMount');
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log('shouldComponentUpdate', nextProps, nextState);
        // 숫자의 마지막 자리가 4면 리렌더링하지 않습니다.
        return nextState.number % 10 !== 4;
    }
    componentWillUnmount() {
        console.log('componentWillUnmount');
    }
    handleClick = () => {
        this.setState({
            number: this.state.number + 1
        });
    };
    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log('getSnapshotBeforeUpdate');
        if (prevProps.color !== this.props.color) {
            return this.myRef.style.color;
        }
        return null;
    }
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log('componentDidUpdate', prevProps, prevState);
        if (snapshot) {
            console.log('업데이트되기 직전 색상: ', snapshot);
        }
    }
    render() {
        console.log('render');
        const style = {
            color: this.props.color
        };
        return (
            <div>
                <h1 style={style} ref={ref => (this.myRef = ref)}>
                    {this.state.number}
                </h1>
                <p>color: {this.state.color}</p>
                <button onClick={this.handleClick}>더하기</button>
            </div>
        );
    }
}
export default LifeCycleSample;
import React, {Component} from 'react';

class ErrorBoundary extends Component{
    state = {
        error : false
    }
    componentDidCatch(error, errorInfo) {
        this.setState({
            error : true
        });
        console.log({error, errorInfo});
    }
    render(){
        return this.state.error ? <div>에러가 발생했습니다</div> : <div>{this.props.children}</div>
    }
}

export default ErrorBoundary;
import React, {Component} from "react";
import LifeCycleSample from './LifeCycleSample';
import ErrorBoundary from './ErrorBoundary';

function randomColor() {
    return "#" + Math.random().toString(16).slice(2,8);
}

class App extends Component{
  state = {
    color : `#000000`,
  }

  render (){
    return (
        <div>
          <button onClick={this.handleClick}> 색상 업데이트 </button>
            <ErrorBoundary>
          <LifeCycleSample color={this.state.color}/>
            </ErrorBoundary>
        </div>
        )
  }

  handleClick =()=> {
    this.setState({
      color : randomColor()
    });
  }
}

export default App;

'React > 리액트를 다루는 기술' 카테고리의 다른 글

9. 컴포넌트 스타일링  (0) 2021.01.25
8. Hooks  (0) 2021.01.22
6. 컴포넌트 반복  (0) 2021.01.17
5. ref: DOM에 이름 달기  (0) 2021.01.15
4. 이벤트 핸들링  (0) 2021.01.12