React Lifecycle of Component

React LIfecycle of Component

리액트 컴포넌트의 라이프 사이클에 대한 글

현재는 클래스형을 많이 사용하지는 않는다고 하지만 그래도 알고 있어야 될 것 같아 정리한다.

먼저 클래스형과 함수형의 차이부터 알아보자!

  • 클래스형
import React, { Component } from 'react';

class App extends Component {
  //클래스로 선언하고 Component를 상속받아 사용해야 한다.
  render() {
    //렌더 메소드가 반드시 필요하다.
    const name = 'react';
    return <div className="react">{name}</div>;
  }
}

export default App;
  • 함수형
import React from 'react';
import './App.css';

function App() {
  const name = 'react';
  return <div className="react">{name}</div>;
}

export default App;
  • 차이
클래스형 함수형
state, lifeCycle 관련 기능 사용 가능 state, lifeCycle 관련기능 사용 못했다.(hook으로 해결)
메모리를 함수형보다는 더 씀 메모리를 클래스형보다 덜 씀
임의 메서드 정의 가능 컴포넌트 선언이 편함
Stateful 컴포넌트 : 로직과 상태를 컴포넌트 내에서 구현, 상대적으로 복잡한 UI Stateless 컴포넌트 : state를 사용하지 않고 단순하게 데이터(props)를 받아서 UI에 뿌려줌
  • 클래스형 컴포넌트 예제
import React, { Component } from 'react';

class Counter extends Component {
  //클래스형 컴포넌트에서는 클래스 내의 constructor 메서드에서 state의 초기값을 설정해줘야 함.
  constructor(props) {
    super(props); //반드시 호출해줘야 함
    this.state = {
      number: 0,
    };
  }
  render() {
    const { number } = this.state; // state 를 조회할 때에는 this.state 로 조회합니다.
    return (
      <div>
        <h1>{number}</h1>
        <button
          // onClick 을 통하여 버튼이 클릭됐을 때 호출 할 함수를 지정합니다.
          onClick={() => {
            // this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

리액트 컴포넌트 라이프사이클

  • 라이프사이클은 크게 세 부분으로 나뉘어진다. (마운트, 업데이트 , 언마운트)

  • 마운트 : DOM이 생성되고 웹 브라우저 상에 나타나는 것

  • 업데이트 : 컴포넌트가 업데이트 되는 경우는 네가지

    • props가 바뀔 때
    • state가 바뀔 때
    • 부모 컴포넌트가 리렌더링 될 때
    • this.forceUpdate로 강제로 렌더링을 트리거 할 때
  • 언마운트

image

출처: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

1. 마운트

/*
*    1. constructor
*    2. getDerivedStateFromProps
*    3. render
*    4. componentDidMount
*/


/*
*	1. constructor : 컴포넌트의 생성자 메서드, 컴포넌트가 만들어질 때 가장먼저 실행
*	리액트에서 생성자의 역할은 다음 두 가지가 있다.
*        - this.state에 객체를 할당하여 지역 state를 초기화
*        - 인스턴스에 이벤트 처리 메서드를 바인딩
*/
constructor(props){
    super(props);
    this.state = { counter: 0 };
    this.handleClick = this.handleClick.bind(this);
}


/*
* 	2. getDerivedStateFromProps : props로 받아온 것을 state에 넣어주고 싶을 때 사용
* 	다른 생명주기 메서드와는 달리 static을 필요로 하고, 이 안에서는 this를 조회 할 수 없음
* 	컴포넌트가 처음 렌더링 되기 전에 호출되고, 그 이후 리렌더링 되기 전에 매번 실행 됨.
* 	마운트 후에 다룰 업데이트에서도 props나 state가 바뀌면 이 함수가 실행 됨.
*/
static getDerivedStateFromProps(nextProps, prevState) {
    console.log("getDerivedStateFromProps");
    if (nextProps.color !== prevState.color) {
      return { color: nextProps.color };
    }
    //여기서 특정 객체를 반환하게 되면, 해당 객체 안에 있는 내용들이 컴포넌트의 state로 설정 됨.
    //null로 반환 시 아무일도 안일어남
    return null;
  }



//3. render : 컴포넌트를 렌더링 하는 메서드
 render() {
    const name = 'react';
    return <div className="react">{name}</div>
  }



/*
*	4. componentDidMount : 컴포넌트의 첫번째 렌더링이 마치고 나면 호출되는 메서드
*	이 메서드가 호출되는 시점에는 우리가 만든 컴포넌트가 화면에 나타나는 상태
*	DOM을 사용해야하는 외부 라이브러리 연동을 하거나, axios, fetch 등을 통하여 요청하거나
*	DOM의 속성을 읽거나 직접 변경
*/

2. 업데이트

/*
*    getDerivedStateFromProps
*    shouldComponentUpdate
*    render
*    getSnapshotBeforeUpdate
*    componentDidUpdate
*/

/*
*	1. getDerivedStateFromProps :  props 나 state 가 바뀌었을때도 이 메서드가 호출된다.
*/

/*
*	2. shouldComponentUpdate : 컴포넌트가 리렌더링 할지 말지를 결정하는 메서드
*	주로 최적화할 때 많이 사용한다고 한다.
*/
shouldComponentUpdate(nextProps, nextState) {
    console.log("shouldComponentUpdate", nextProps, nextState);
    // 숫자의 마지막 자리가 4면 리렌더링하지 않습니다
    return nextState.number % 10 !== 4;
  }


/*
*	3. render 생략
*/


/*
*	4. getSnapshotBeforeUpdate : 컴포넌트에 변화가 일어나기 직전의 DOM의 상태를 가져와서
*	특정 값을 반환하면 그 다음 발생하는 componentDidUpdate 함수에서 받아와서 사용을 할 수 있다.
*/
 getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate");
    if (prevProps.color !== this.props.color) {
      return this.myRef.style.color;
    }
     //반환한 값으로 그 다음 발생하는 componentDidUpdate 함수에서 받아와서 사용을 할 수 있다.
    return null;
  }


/*
*	5. componentDidUpdate : 리렌더링이 마치고, 화면에 우리가 원하는 변화가
*	모두 반영되고 난 뒤 호출되는 메서드
*	세 번째 파라미터로 4번의 getSnapshotBeforeUpdate에서 반환한 값을 조회할 수 있다.
*/

componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("componentDidUpdate", prevProps, prevState);
    if (snapshot) {
      console.log("업데이트 되기 직전 색상: ", snapshot);
    }
  }

3. 언마운트

// componentWillUnmount : 컴포넌트가 화면에서 사라지기 전에 호출
// DOM에 직접 등록했었던 이벤트 제거, setTimeout과 같은 것이 있으면 clearTimeout을 통하여 제거
 componentWillUnmount() {
    console.log("componentWillUnmount");
  }

📕리액트 문서의 Hook Motivation 📕

Class은 사람과 기계를 혼동시킵니다.

Class가 코드의 재사용성과 코드 구성을 좀 더 어렵게 만들 뿐만 아니라, React를 배우는데 큰 진입장벽이라는 것을 알게 되었습니다. Javascript에서 어떻게 this가 작동하는지 알아야만 했고, 대부분의 다른 언어와는 다르게 작동합니다. 이벤트 핸들러가 등록되는 방법을 기억해야만 합니다. 불안정한 문법 제안들이 없다면, 코드는 매우 장황해집니다. 사람들은 props, state, 그리고 top-down 데이터 흐름을 완벽하게 이해할 수 있지만, 여전히 Class는 쉽지 않습니다. React 안에서의 함수와 Class 컴포넌트들을 구별하고 각 요소를 언제 사용하는지는 숙련된 React 개발자 사이에서도 의견이 일치하지 않습니다.

이러한 문제를 해결하기 위해, Hook은 Class없이 React 기능들을 사용하는 방법을 알려줍니다. 개념적으로 React 컴포넌트는 항상 함수에 더 가깝습니다. Hook은 React의 정신을 희생하지 않고 함수를 받아들입니다. Hook은 명령형 코드로 해결책을 찾을 수 있게 해주며 복잡한 함수형 또는 반응형 프로그래밍 기술을 배우도록 요구하지 않습니다.

그래서 나온게 Hook!

  • this.state -> useState
  • componentDidMount, componentDidUpdate, componentWillUnmount -> useEffect

💫useEffect!!

  1. 컴포넌트가 마운트 됐을 때 (처음 나타났을 때)

  2. 언마운트 됐을 때 (사라질 때)

  3. 업데이트 될 때 (특정 props가 바뀔 때) 특정 작업을 처리하는 Hook

  1. 코드를 먼저 보자
useEffect(() => {
  console.log('컴포넌트가 화면에 나타남');
  return () => {
    console.log('컴포넌트가 화면에서 사라짐'); //cleanup함수
  };
}, []);
  • useEffect(함수, 의존배열(deps)) : useEffect의 첫 번째 파라미터에는 함수, 두 번째 파라미터에는 의존값이 들어있는 배열(deps) 를 넣는다.

  • deps 의존배열

    • 빈 값[]이면 컴포넌트가 처음 나타날때만 useEffect에 등록한 함수가 호출된다.
    • 설정해주지 않으면 useEffect(()=> {console.log('리렌더링')})컴포넌트가 리렌더링 될 때만 함수가 호출된다.
  • cleanup함수 : useEffect에서는 반환하는 함수를 cleanup 함수라고 부른다. useEffect에 대한 뒷정리를 해주고, deps가 빈 값이면 컴포넌트가 사라질 때 cleanup함수가 호출

  • 콘솔 화면

image

컴포넌트 세 개가 마운트 될 때 컴포넌트가 화면에 나타남이 출력되고

삭제 버튼을 눌렀을 때 컴포넌트가 화면에서 사라짐이 출력된다.