본문 바로가기

프론트엔드(Front-End)/React

[REACT] 리액트의 라이프사이클(LifeCycle)

리액트의 라이프사이클(LifeCycle)

  • 리액트의 각 컴포넌트는 생성되고 사라지는 일련의 생애주기(LifeCycle)를 가집니다.
  • 단, 라이프 사이클은 리액트의 컴포넌트 중 클래스형 컴포넌트에만 적용 가능합니다.
  • 함수형 컴포넌트는 Hook을 통해 얼추 이 라이프 사이클을 흉내낼 수는 있지만, 기본적으로 라이프 사이클은 클래스형 컴포넌트에서 사용 가능합니다.

리액트의 라이프사이클 흐름도(Life Cycle’s Flow)

  • 리액트는 크게 마운트 - 업데이트 - 언마운트 의 흐름을 따른다고 설명합니다.
  • 마운트는 리액트 컴포넌트가 화면에 등장할 때 한 번 동작하는 단계로, constructor 등으로 state를 설정하는 등의 작업을 하는 단계입니다.
  • 업데이트는 컴포넌트의 input 상자에 값이 바뀐다거나, 클릭 이벤트로 리렌더링 하는 등의 경우에 거치는 단계로, 컴포넌트가 화면에서 완전히 사라진 것이 아니면 데이터가 바뀌거나 입력값을 받는 등 거의 대부분의 상호 작용에 대응할 수 있기 때문에 리액트의 컴포넌트 생애 주기 중에서 가장 많은 시간을 점유하는 단계 입니다.
  • 언마운트는 리액트 컴포넌트가 화면에서 사라질 경우 거치는 단계로 이 단계를 거치면 리액트 컴포넌트의 라이프 사이클은 종료 됩니다.

 


 

 

마운트(Mount, Mounting)

마운트 단계의 메서드

constructor

 constructor(props){
        super(props);
        console.log('constructor');
}
  • 생성자 메서드
  • 초기 state의 값을 설정 해주는 데 사용합니다.

render

render() {
    console.log('render');
    const style = {
        color:this.props.color,
    }
    return(
        <>
            <div className="sample">
						</div>
				</>
		)
}
  • 리액트 라이프 사이클의 모든 메서드 중에서 유일하게 필수 메서드 입니다.
  • 이 메서드 안에서는 이벤트 설정이 아닌 곳에서 setState를 사용하면 안됩니다.
  • 브라우저에 DOM에도 접근 할 수 없습니다.
  • DOM 정보를 가져오거나 state에 변화를 주고 싶을 때는 componentDidMount에서 처리해야 합니다.

getDerivedSateFromProps

static getDerivedStateFromProps(nextProps, prevProps){
    console.log("getDerivedSateFromProps");
    if(nextProps.color !== prevProps.color){
        return {color:nextProps.color}
    }
    return null;
}
  • 리액트 v16.3 이후 도입된 기능입니다.
  • props로 받아온 값을 state에 동기화 시키는 용도로 사용됩니다.

componentDidMount

componentDidMount(){
    console.log('componentDidMount');
}
  • 컴포넌트를 만들고 첫 렌더링을 다 마친 후의 상태입니다.
  • 다른 자바스크립트 라이브러리, 프레임워크 함수, 이벤트 등록, setTimeout, setInterval, 네트워크 요청 같은 비동기 작업을 처리하는 데 쓰입니다.

 


 

 

업데이트(Update, Updation)

 

업데이트 단계의 메서드

shouldComponentUpdate(nextProps, nextState) { . . . }

shouldComponentUpdate(nextProps, nextState){
    console.log('shouldComponentUpdate', nextProps, nextState);
    return nextState.number % 10 !== 4; // 끝자리가 4일 때는 렌더링 안함.
}
  • props, state 변경 시 리렌더링 시작할 지 지정하는 메서드입니다.
  • 반드시 boolean 값을 리턴 해야 하며, 리액트 페이지의 전체 오류가 발생(백지현상)합니다.

getSnapshotBeforUpdate(prevProps, prevState) { . . . }

getSnapshotBeforeUpdate(prevProps, prevState){
    console.log('getSnapshotBeforeUpdate');
    if(prevProps.color !== this.props.color){
        return this.myRef.style.color;
    }
    return null;
}
  • 리액트 v16.3. 이후 등장하였습니다.
  • render에서 만들어진 결과물이 실제 반영되기 직전에 호출합니다.
  • compoentDidUpdate 이전 메서드이므로 이 단계에서 리턴하는 값이 compoentDidUpdate 메서드의 snapshot 매개변수가 됩니다.
  • 업데이트하기 직전의 값을 참고하는데 주로 사용되며 대표적으로 스크롤바 위치 유지 등에 응용됩니다.

componentDidUpdate(prevProps, prevState, snapshot) { . . . }

componentDidUpdate(prevProps, prevState, snapshot){
    console.log('componentDidUpdate', prevProps, prevState, snapshot);
    if(snapshot){
        console.log('업데이트 직전의 색상 :', snapshot);
    }
}
  • 리렌더링 완료 후 실행합니다.
  • 업데이트 직후이므로 DOM 관련 처리가 가능합니다.
  • prevProps또는 prevState에 접근하여 컴포넌트가 이전에 가졌던 데이터에 접근 가능합니다.
  • getSnapshotBeforUpdate에서 리턴한 값이 있다면 snapshot 변수로 받아 사용 가능합니다.

 


 

 

언마운트(UnMount, UnMounting)

 

 

언마운트 단계의 메서드

componentWillUnmount

componentWillUnmount(){
        console.log('componentWillUnmount');
    }
  • 컴포넌트를 DOM에서 제가할 때 실행합니다.
  • componentDidMount에서 생성한 DOM, event 등이 있다면 제거해야 합니다.

 

 


 

 

에러 (Error)

  • 컴포넌트의 일반적인 라이프 사이클은 아닙니다.
  • 컴포넌트에서 오류를 감지하면 캐치하고 오류 페이지를 렌더링하는데 응용 가능합니다.

 

compoentDidCatch

  • 컴포넌트의 예외 처리를 담당합니다.
  • 실제 렌더링 되는 컴포넌트에서 해당 메서드를 사용하면 에러를 잡을 수 없습니다.
  • 이 메서드를 사용하면 부모 컴포넌트에서 넘겨주는 children 프로퍼티를 통해 오류를 검출하므로, 예외 처리를 원하면 별도의 예외처리 담당 컴포넌트를 작성하고 실제 렌더링 될 컴포넌트를 감싸는 식으로 코드를 작성해야 합니다.

 

에러 핸들링 컴포넌트는 아래와 같이 작성할 수 있습니다.

import { Component } from "react";

class ErrorHandlingComp extends Component {
  state = {
    isError: false,
  };
  componentDidCatch(error, info) {
    this.setState({
      isError: true,
    });
    console.log({ error, info });
  }

  render() {
    if (this.state.isError) return <div className="error-div">에러가 발생했습니다.</div>;
    return this.props.children;
  }
}

export default ErrorHandlingComp;

 

App.js (부모 컴포넌트) 에서 에러를 핸들링 하도록 작성합니다.

import './App.css';
import './asset/Custom.css';
import './asset/LifeCycleComp.css'

import {Fragment, Component} from "react";
import LifeCycleComp01 from "./components/LifeCycleComp01";
import ErrorHandlingComp from './components/ErrorHandlingComp';

function getRandomColor() {
  return '#' + Math.floor(Math.random()*16777215).toString(16);
}

class App extends Component {
  state = {
    color :'#000000',
    name : 'LifeCycle'
  }

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

  render() {
    const {name} = this.state;
    return(
    <>
      <div className="main-header">
        <p>React의 <span>{name}</span> 에 대해 알아보기</p>  
        <button onClick={this.handleClick}>랜덤 색상</button>            
      </div>
      <ErrorHandlingComp>
        <LifeCycleComp01 color={this.state.color} />
      </ErrorHandlingComp>
    </>
    )
  }
}

export default App;
  • 오류 발생 시 렌더링 된 페이지


 

 

클래스형 컴포넌트의 라이프사이클 적용 예시

import { Fragment, Component } from "react";

class LifeCycleComp01 extends Component{
    state = {
        number: 0,
        color:null,
    }

    myRef = null; // ref를 설정하기 위한 부분
    
    constructor(props){
        super(props);
        console.log('constructor');
    }
    
    static getDerivedStateFromProps(nextProps, prevProps){
        console.log("getDerivedSateFromProps");
        if(nextProps.color !== prevProps.color){
            return {color:nextProps.color}
        }
        return null;
    }

    componentDidMount(){
        console.log('componentDidMount');
    }

    shouldComponentUpdate(nextProps, nextState){
        console.log('shouldComponentUpdate', nextProps, nextState);
        return nextState.number % 10 !== 4; // 끝자리가 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, snapshot);
        if(snapshot){
            console.log('업데이트 직전의 색상 :', snapshot);
        }
    }

    render() {
        console.log('render');
        const style = {
            color:this.props.color,
        }

        return(
            <>
                <div className="comp class-comp life-comp">
                    <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>
                </div>
            </>
        )
    }

}

export default LifeCycleComp01;
  • 실제 렌더링된 페이지 모습