[React] 9. 라이프사이클 메서드

1. 라이프사이클 메서드 정리 ✍

1.1 render() 함수

render() {...}
  • 라이프사이클 메서드 중 유일한 필수 메서드이다.
  • 이 메서드 안에서 this.propsthis.state에 접근할 수 있으며, 리액트 요소를 반환한다.
    예를 들어, div 태그, 컴포넌트

주의할점❗❗

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

1.2 constructor 메서드

constructor(props) {...}
  • 컴포넌트의 생성자 메서드로 컴포넌트를 만들 때 처음으로 실행되고, 초기 state를 정할 수 있다.

1.3 getDerivedStateFromProps 메서드

  • 리액트 v16.3 이후에 만들어진 메서드로, props로 받아 온 값을 state에 동기화시키는 용도로 사용하고 컴포넌트가 마운트될 때와 업데이트될 때 호출된다.
static getDerivedStateFromProps(nextProps, prevState) {
  if(nextProps.value !== prevState.value) { // 조건에 따라 특정 값 동기화
    return { value: nextProps.value };
  }
  return null; // state를 변경할 필요가 없다면 null 반환
}

1.4 componentDidMount 메서드

componentDidMount() {...}
  • 컴포넌트를 만들고, 첫 렌더링을 마친 후 실행하는 메서드이다.
  • 이 메서드를 통해 다른 자바스크립트 라이브러리 또는 프레임워크의 함수를 호출, 이벤트 등록, setTimeout, setInterval, 네트워크 요청 같은 비동기 작업 처리한다.

1.5 shouldComponentUpdate 메서드

shouldComponentUpdate(nextProps, nextState) {...}
  • props or state를 변경했을 때, 리렌더링을 시작할지 여부를 지정하는 메서드이다.
  • 위의 언급대로 true, false 반환하며, 컴포넌트를 만들 때 이 메서드를 따로 사용하지 않으면 기본값은 true를 반환한다.❗❗
  • 현재 propsstatethis.propsthis.state로 접근하고, 새로 설정될 propsstatenextProps, nextState로 접근한다.
    TIP ❗❗
    프로젝트 성능을 최적화할 때, 상황에 맞는 알고리즘을 사용하여 리렌더링을 방지할 때는 false 값을 반환하면 된다.

1.6 getSnapshotBeforeUpdate 메서드

  • 리액트 v16.3 이후 만들어진 메서드로 render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출된다.
  • 이 메서드에서 반환하는 값은 componentDidUpdate에서 세 번째 파라미터snapshot 값으로 전달받으며, 주로 업데이트하기 직전의 값을 참고할 때 사용한다.
    👉 (ex. 스크롤바 위치 유지)
getSnapshotBeforeUpdate(prevProps, prevState) {
  if(prevState.array !== this.state.array) {
    const { scrollTop, scrollHeight } = this.list
    return { scrollTop, scrollHeight };
  }
}

1.7 componentDidUpdate 메서드

componentDidUpdate(prevProps, prevState, snapshot) {...}
  • 리렌더링을 완료 후 실행하는 메서드로, 업데이트가 끝난 직후라서 DOM 관련 처리를 해도 상관없다.
  • 여기서 prevProps or prevState를 사용해서 컴포넌트가 이전에 가졌던 데이터에 접근할 수 있다.
  • snapshot 값은 getSnapshotBeforeUpdate에서 반환한 값으로 전달받을 수 있다.

1.8 componentWillUnmount 메서드

componentWillUnmount() {...}
  • 컴포넌트를 DOM에서 제거할 때 사용한다.
  • componentDidMount에서 등록한 이벤트, 타이머, 직접 생성한 DOM이 있다면 여기서 제거해야 한다.

1.9 componentDidCatch 메서드

  • 리액트 v16에 새롭게 생성된 메서드로, 컴포넌트 렌더링 도중에 에러가 발생했을 때 오류 UI를 보여 줄 수 있다.
componentDidCatch(error, info) {
  this.setState({
    error: true
  });
  consol.log({ error, info });
}
  • error는 파라미터에 어떤 에러가 발생했는지 알려주고, info는 어디에 있는 코드에서 오류가 발생했는지에 대한 정보를 담고있다.

알고 넘어가기 ❗❗

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

2. 라이프사이클 메서드 실습

2.1 컴포넌트 생성

  • src/LifeCycleSample.js를 생성하여 아래와 같이 코드를 작성했다.
//LifeCycleSample.js
class LifeCycleSample extends Component {
    state = {
        number: 0,
        corlor: null,
    }

    myRef = null; // ref를 설정할 부분

    constructor(props) {
        super(props);
        console.log('constructor');
    }

    static getDerivedStateFormProps(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.setState.number + 1
        });
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log('getSnapshotBeforeUpdate');
        if(prevProps.color !== this.prevProps.color) {
            return this.myRef.style.color;
        }
        return null;
    }

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

    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.props.color}</p>
                <button onClick={this.handleClick}>
                    더하기
                </button>
            </div>
        )
    }
}
  • 이 컴포넌트는 각 라이프사이클 메서드를 실행할 때마다 console.log를 출력하고, 부모 컴포넌트에서 props로 색상을 받아 버튼을 누르면 state.number 값이 1씩 증가한다.
  • getDerivedStateProps는 부모에게서 받은 color 값을 state에 동기화하고 있으며, getSnapshotBeforeUpdateDOM에 변화가 일어나기 적진의 색상 속성을 snapshot 값으로 변환하여 이것을 componentDidUpdate에서 조회할 수 있다.
  • 그리고 shouldComponentUpdate 메서드에서 state.number 값의 마지막 자리 수가 4(ex. 4, 14, 24, 34 등등)로 끝나면 리렌더링을 취소한다.

2.2 LifeCycleSample 렌더링

  • src/App.js 파일에 다음과 같이 코드를 작성했다.
//App.js
// 랜덤 색상 생성하는 함수
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>
    );
  }
}
  • getRandomColor 함수로 랜덤 색상을 설정했는데, 16777215는 hex 표현으로 ffffff이다.
  • 버튼을 렌더링하고, 누를 때마다 handleClidk 메서드가 호출되게 이벤트를 설정했고, 불러온 LifeCycleSample 컴포넌트에 color 값을 props로 설정한다.

주의 ❗❗

  • LifeCycleSample.js 에서 <p>color: {this.state.color}</p> 구문을 <p>color: {this.props.color}</p> 로 고쳤다.
    * 나도 모르게 state 값을 받아왔는데 propscolor 값을 받아왔던 점 주의하자.

2.3 에러 잡아내기

  • 방금 만든 LifeCycleSample 컴포넌트의 render 함수에서 에러가 발생할 경우 브라우저에 아무것도 출력되지 않는다.
  • 따라서 사용자들이 당황하지 않도록 src/ErrorBoundary.js 라는 컴포넌트를 생성하여 아래의 코드를 작성했다.
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;
    }
}
  • 에러가 발생하면 componentDidCatch 메서드가 호출되어 this.state.error 값을 true로 업데이트한다.
  • 그 후, render 함수는 this.state.error 값이 true라면 에러가 발생했음을 알려주는 div 태그를 보여준다.
  • 사용 방법은 LifeCycleSample 컴포넌트를 감싸주면 된다.

사용자에게 에러 출력 안되도록 주의✔
그리고 state와 props가 햇갈리는데 계속 반복해서 복습해야겠다. ㅠㅠ🤦‍♂️

end

좋은 웹페이지 즐겨찾기