공부블로그

리액트 7장. 컴포넌트의 라이프사이클 메서드 본문

리액트/리액트를 다루는 기술

리액트 7장. 컴포넌트의 라이프사이클 메서드

떠어영 2021. 1. 7. 04:30

모든 리액트 컴포넌트에는 라이프사이클( 수명주기 )이 존재한다. 

컴포넌트의 수명은 페이지에 렌더링되기 전인 준비 과정에서 시작하여 페이지에서 사라질 때 끝난다.

 

리액트 프로젝트를 진행하다 보면 컴포넌트를 처음으로 렌더링할 때 어떤 작업을 처리해야 하거나, 컴포넌트를 업데이트 하기 전후로 어떤 작업을 처리해야 할 수도 있고, 불필요한 업데이트를 방지해야 할 수도 있다.

이때 컴포넌트의 라이프사이클 메서드를 사용한다.

(라이프사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있고, 함수형에서는 Hooks기능을 사용해 비슷한 작업을 처리할 수 있다.)

 

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

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

 

Will 접두사가 붙은 메서드는 어떤 작업을 작동하기 에 실행되는 메서드이고,

Did 접두사가 붙은 메서드는 어떤 작업을 작동한 에 실행되는 메서드이다.

이 메서드들은 컴포넌트 클래스에서 덮어 써 선언함으로써 사용할 수 있다.

 

라이프사이클은 총 3가지 ( 마운트, 업데이트, 언마운트 ) 카테고리로 나눈다.

 

   - 마운트 : DOM이 생성되고 웹 브라우징에 나타나는 것

 

        컴포넌트 만들기 → constructor getDerivedStateFromProps render componentDidMount  

 

   - 업데이트 : 총 4가지 경우에 업데이트

 

        1. props가 바뀔 때 2. state가 바뀔 때 3. 부모 컴포넌트가 리렌더링될 때 getDerivedStateFromProps

         → shouldComponentUpdate ( true반환 시 render 호출, false반환 시여기서 작업 취소) render ( ← 4. this.

         forceUpdate 로 강제로 업데이트될 때 )  getSnapshotBeforeUpdate( 웹 브라우저상의 실제 DOM변화 )  

         → componentDidupdate  

 

   - 언마운트 : 컴포넌트를 DOM에서 제거하는 것

     

7. 2 ) 라이프사이클 메서드 살펴보기

7. 2. 1 ) render( )함수

  • 컴포넌트 모양새를 정의, 필수 메서드 

  • 메서드 안에서 this. props와 this. state에 접근할 수 있고 리액트 요소를 반환

  • 이벤트 설정이 아닌 곳에서 setState사용 X, 컴포넌트의 DOM요소에 접근 X ( state값에 변화를 줄 때는 componentDidMount에서 처리 )

7. 2. 2 ) constructor 메서드

  • constructor( props ) { ... }

  • 컴포넌트의 생성자 메서드, 초기 state설정

7. 2. 3 ) getDerivedStateFromProps 메서드

  • props로 받아 온 값을 state에 동기화, 컴포넌트가 마운트 될 때와 업데이트될 때 호출

static getDerivedStateFromProps(nextProps, prevState) {
	if(nextProps.value !== prevState.value) { // 조건에 따라 특정 값 동기화
    	return { value: nextProps.value };
        }
    return null; // state를 변경할 필요가 없다면 null반환
}

7. 2. 4 ) componentDidMount 메서드

  • componentDidMount( ) { ... }

  • 컴포넌트를 다 만들고, 첫 렌더링을 마친 후 실행

  • 이 안에서 다른 자바스크립트 라이브러리 또는 프레임워크의 함수를 호출하거나 이벤트 등록, setTimeout...같은 비동기 작업을 처리

7. 2. 5 ) shouldComponentUpdate 메서드

  • shouldComponentUpdate( nextProps, nextState ) { ... }

  • props 또는 state를 변경했을 때, 리렌더링을 시작할지 여부를 지정

  • true 또는 false값을 반환 ( false이면 업데이트 과정 중지 )

  • 현재 props와 state는 this. props와 this. state로 접근

  • 새로 설정될 props와 state는 nextProps와 nextState로 접근

7. 2. 6 ) getSnapshotBeforeUpdate 메서드

  • render에서 만들어진 결과물이 브라우저에 반영되기 직전에 호출

  • 반환한 값은 componentDidUpdate에서 세번째 파라미터인 snapshot으로 전달받을 수 있다

  • 주로 업데이트하기 직전의 값을 참고할 때 활용 ex) 스크롤바 위치 유지

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

7. 2. 7 ) componentDidUpdate 메서드

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

  • 리렌더링을 완료한 후 실행, DOM 관련 처리 무방

  • prevProps, prevState를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근

7. 2. 8 ) componentWillUnmount 메서드

  • componentWillUpdate( ) { ... }

  • 컴포넌트를 DOM에서 제거할 때 실행

7. 2. 9 ) componentDidCatch 메서드

  • 컴포넌트 렌더링 도중 에러가 발생했을 때 오류 UI를 보여준다

  • 컴포넌트 자신에게 발생하는 에러는 잡을 수 없고 this.props.children으로 전달되는 컴포넌트에서 발생하는 에러만 잡을 수 있다.

componentDidCatch(error, info) { //error는 어떤 에러인지, info는 어디인지 알려준다.
	this.setState({
    	error: true
        });
    console.log({ error, info });
}

 

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

LifeCycleSample. js 

import React, { Component } from "react";

class LifeCycleSample extends Component {
  state = {
    number: 0,
    color: null,
  };
  myRef = null;

  constructor(props) {
    super(props);
    console.log("constructor");
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("getDerivedStateFromProps");
    if (nextProps.color !== prevState.color) {
      return { color: nextProps.color }; //부모에게서 받은 color값(props)을 state에 동기화
    }
    return null;
  }
  componentDidMount() {
    console.log("componentDidMount");
  }
  shouldComponentUpdate(nextProps, nextState) { //false or true 값을 받아서 리렌더링 여부 결정
    console.log("shouldComponentUpdate", nextProps, nextState);
    return nextState.number % 10 !== 4; //숫자의 마지막 자리가 4이면 렌더링하지 않는다.
  }
  componentWillUnmount() {
    console.log("componentWillUnMount");
  }
  handleclick = () => {
    this.setState({
      number: this.state.number + 1, 
      //'더하기'버튼의 onClick이벤트핸들링을 위해 number state 업데이트
    });
  };
  getSnapshotBeforeUpdate(prevProps, prevState) {
    //???
    console.log("getSnapshotBeforeUpdate");
    if (prevProps.color !== this.props.color) {
      return this.myRef.style.color; //DOM요소의 변화 직전 색상을 snapshot으로 반환
      //myRef는 <h1>이고 그 요소에서의 color 반환
    }
    return null;
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("componentDidUpdate", prevProps, prevState);
    if (snapshot) {
      console.log("업데이트 되기 직전 색상", snapshot); //snapshot조회
    }
  }
  render() {
    console.log("render");
    const style = {
      color: this.props.color, // 부모에서 설정한 props로 스타일 설정
    };
    
    return (
      <div>
        <h1 style={style} ref={(ref) => (this.myRef = ref)}>
          {this.state.number}
        </h1>
        <p>color: {this.state.color} </p>
        {/* this.state.color대신 this.props.color라고 해도 되는데..?*/}
        <button onClick={this.handleclick}>더하기</button>
      </div>
    );
  }
}
export default LifeCycleSample;

각 라이프사이클 메소드를 실행할 때 마다 콘솔 디버거에 기록하고,

부모 컴포넌트에서 props로 색상을 받아 버튼을 누르면 state.number 값을 1씩 더한다.

getDerivedStateProps로 부모에게서 받은 color값을 state에 동기화 ( <p>color: {this.state.color}</p> 에서 this.state.color == this.props.color )

getSnapshotBeforeUpdate는 DOM에 변화가 일어나기 직전의 색상 속성을 snapshot값으로 반환, componentDidUpdate에서 조회

shouldComponentUpdate에서 state.number값의 마지막 자리수가 4이면 렌더링을 취소하도록 설정

 

 

App. js

import React, { Component } from "react";
import LifeCycleSample from "./LifeCycleSample";

//랜덤 색상을 생성
function getRandomColor() {
  return "#" + Math.floor(Math.random() * 16777215).toString(16);
}
class App extends Component {
  state = {
    color: "#000000", //color state
  };
  handleClick = () => {
    this.setState({
      color: getRandomColor(), //랜덤 색상 생성해서 color state에
    });
  };
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>랜덤 색상</button>{" "}
        {/*버튼을 누르면 랜덤함수로 color 업데이트*/}
        <LifeCycleSample color={this.state.color} />{" "}
        {/* props로 color를 설정해서 랜덤색상을 받음 */}
      </div>
    );
  }
}
export default App;

 

(실행화면)

랜덤색상 버튼을 누르면 글자색과 color값이 업데이트 되고, 마지막 자리수가 4인경우에는 렌더링되지 않는다.

(13 다음에 더하기 버튼을 누르면 변화가 없고 한번 더 누르면 15가 된다)   

 

 

7. 3. 1 ) 에러 잡아내기 

 

에러를 잡아주는 ErrorBoundary라는 컴포넌트를 생성해보자.

 

ErrorBoundary. js

import React, { Component } from "react";

class ErrorBoundary extends Component {
  state = {
    error: false,
  };
  componentDidCatch(error, info) {
    //error state를 true로 업데이트
    this.setState({
      error: true,
    });
    console.log({ error, info });
  }
  render() {
    if (this.state.error) return <div>에러가 발생했습니다.</div>; //error == true
    return this.props.children;
  }
}
export default ErrorBoundary;

      

App. js에서 LifeCycleSample컴포넌트를 감싸준다.

import ErrorBoundary from "./ErrorBoundary"; 추가
...
<ErrorBoundary>
          <LifeCycleSample color={this.state.color} />
</ErrorBoundary>

 

 

이번 장에서는 컴포넌트의 라이프사이클과 라이프사이클 메서드에 대해 알아보았다.

라이프사이클 메서드는 컴포넌트 상태 변화가 있을 때마다 실행하는 메서드이고, 서드파티 라이브러리를 사용하거나 DOM을 직접 건드려야 하는 상황에서 유용하다.