[React] 컴포넌트 생명주기

zero_0·2022년 1월 21일
0

FE 학습

목록 보기
16/22
post-thumbnail

컴포넌트 생명주기

  • 모든 리액트 컴포넌트에는 라이프사이클이 존재합니다. 컴포넌트의 수명은 페이지에 렌더링되기 전인 준비 과정에서 시작하여 페이지에서 사라질 때 끝납니다.
  • 컴포넌트의 라이프사이클 메서드를 사용!
  • 함수 컴포넌트에서는 사용불가 Hooks를 사용하면 가능

라이프사이클 매서드의 이해

라이프사이클 메서드

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

Will이 붙은 메서드는 어떤 작업 전에 실행되는 메서드
Did가 붙은 메서드는 어떤 작업 후에 실행되는 메서드

라이프사이클 메서드 살펴보기

1. render() 함수

☀️ 컴포넌트 모양새를 정의한다.
컴포넌트에서 가장 중요한 메서드, 필수 메서드이다
이 메서드 안에서 this.propsthis.state에 접근할 수 있으며, 리액트 요소를 반환한다.

⚠️ 이 메서드 안에서는 이벤트 설정이 아닌 곳에서
setState를 사용하면 안되며, 브라우저의 DOM에도 접근해서는 안된다.
DOM정보를 가져오거나 state에 변화를 줄 때는 componentDidMount에서 처리해야 한다.

2. constructor 메서드

☀️ constructor(props) {...}
컴포넌트의 생성자 메서드로 컴포넌트를 만들 때 처음으로 실행된다.
이 메서드에서는 초기 state를 정할 수 있다.

3. getDerivedStateFromProps 메서드

☀️ props로 받아온 값을 state에 동기화시키는 용도로 사용하며, 컴포넌트가 마운트될 때와 업데이트 될 때 호출된다.

4. componentDidMount 메서드

<aside>
☀️ 컴포넌트를 만들고, 첫 렌더링을 다 마친 후 실행한다.
이 안에서 다른 자바 스크립트 라이브러리 또는 프레임워크의 함수를 호출하거나 이벤트 등록, `setTimeout`, `setInterval` , 네트워크 요청 같은 비동기 작업을 처리하면 된다.

</aside>

5. shouldComponentUpdate 메서드 💫

☀️ props 또는 state를 변경했을 때, 리렌더링을 시작할지 여부를 지정하는 메서드.
반드시 true 값과 false값을 반환 해야한다.
컴포넌트를 만들 때 이 메서드를 따로 생성하지 않으면
기본적으로 언제나 true값을 반환한다.
이 메서드가 false 값을 반환한다면 업데이트 과정은 여기서 중지된다.

이 메서드 안에서 **현재** props와 state는 `this.props`와 `this.state`로 접근하고, **새로 설정**될 props 또는 state는 `nextProps`와 `nextState`로 접근할 수 있다.

</aside>
  1. getSnapshotBeforeUpdate 메서드

    ☀️ render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출된다. ex) 스크롤바 위치 유지
  2. componentDidUpdate 메서드

    ☀️ 리렌더링을 완료한 후 실행합니다. 업데이트가 끝난 직후이므로, `DOM`관련 처리를 해도 무방하다. 여기서는 `prevProps`또는 `prevState`를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근할 수 있다. 또 `getSnapshotBeforeUpdate`에서 반환한 값이 있다면 여기서 `snapshot`값을 전달받을 수 있다.
  3. componentWillUnmount 메서드

    ☀️ 컴포넌트를 `DOM`에서 제거할 때 실행한다. `componentDidMount`에서 등록한 이벤트, 타이머, 직접 생성한 `DOM`이 있다면 여기서 제거해야 한다.
  4. componentDidCatch 메서드

    ☀️ 컴포넌트 렌더링 도중에 에러가 발생했을 때 애플리케이션이 먹통이 되지 않고 **오류** `UI`를 보여줄 수 있게 해준다. `error`은 파라미터에 어떤 에러가 발생했는지 알려주고, `info` 파라미터는 어디에 있는 코드에서 오류가 발생했는지에 대한 정보를 준다.

라이프 사이클

  • 라이프사이클은 총 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};
            }
            return null;
        }
    
        componentDidMount() { //마운트
            console.log("componentDidMount");
        }
    
        shouldComponentUpdate(nextProps, nextState) { //업데이트 : 끝자리가 4로 끝나면 리렌더링 하지 않음 true, flase로 판단 true일때 render함수 실행
            console.log("shouldComponentUpdate", nextProps, nextState);
            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;
  • 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",
      };
    
      handleClick = () => {
        this.setState({
          color: getRandomColor(),
        });
      };
    
      render() {
        return (
          <div>
            <button onClick={this.handleClick}>랜덤 색상</button>
            <LifeCycleSample color={this.state.color} />
          </div>
        );
      }
    }
    
    export default App;

에러 잡아내기

  • 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 };
        }
        return null;
      }
    
      componentDidMount() {
        //마운트
        console.log("componentDidMount");
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        //업데이트 : 끝자리가 4로 끝나면 리렌더링 하지 않음 true, flase로 판단 true일때 render함수 실행
        console.log("shouldComponentUpdate", nextProps, nextState);
        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>
            {this.props.missing.value}
            <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;
  • ErrorBoundary.js 추가
    import React, { Component } from 'react';
    
    class ErrorBoundary extends Component {
        state = {
            error:false
        };
    
        componentDidCatch(error, info) {//에러났을 때 나오는 구문 지정
            this.setState({
                error:true
            });
            console.log({error, info});
        };
        render() {
            if (this.state.error) return <div>에러가 발생했습니다!</div>;
            return this.props.children;
        }
    }
    
    export default ErrorBoundary;
  • App.js 변경된 부분
    import React, { Component } from "react";
    import LifeCycleSample from "./LifeCycleSample";
    import ErrorBoundary from "./ErrorBoundary";
    
    //랜덤 색상 생성
    function getRandomColor() {
      return "#" + Math.floor(Math.random() * 16777215).toString(16);
    }
    
    class App extends Component {
      state = {
        color: "#000000",
      };
    
      handleClick = () => {
        this.setState({
          color: getRandomColor(),
        });
      };
    
      render() {
        return (
          <div>
            <button onClick={this.handleClick}>랜덤 색상</button>
            <ErrorBoundary>
              <LifeCycleSample color={this.state.color} />
            </ErrorBoundary>
          </div>
        );
      }
    }
    
    export default App;
  • 마운트 : DOM이 생성되고 브라우저 상에 나타나는 것

    업데이트 : 4가지 경우에 업데이트한다.

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

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

profile
차근차근 채워가는 it일지

0개의 댓글