[velopert] 리액트 강의(3)

kirin.log·2021년 2월 22일
1

📚 LifeCycle API

LifeCycle API컴포넌트가 브라우저에서 나타날때, 사라질때, 그리고 업데이트 될 때, 호출되는 API
즉, 컴포넌트가 DOM 위에 생성되기 전/후나, 데이터가 변경되어 상태를 업데이트하기 전/후로 실행되는 메소드들을 의미한다.


🎃 component안에서 method의 실행순서


👉 컴포넌트 생성시: constructor -> componentWillMount -> render -> componentDidMount 순으로 진행

👉 컴포넌트 제거시: componentWillUnmount 메소드만 실행

👉 컴포넌트의 props 변경시: componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate 순으로 진행

👉 컴포넌트의 state 변경시: shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate 순으로 진행


🪐 컴포넌트 초기 생성
컴포넌트가 브라우저에 나타나기 , 에 호출되는 API
= Mounting

🧱 constructor

  • 컴포넌트 생성자 함수 (초기값 설정 등) ➡ 가장 먼저 실행되는 함수
  • 컴포넌트가 새로 만들어질 때마다 이 함수가 호출
  • 여기서 기본 state를 초기설정 할 수 있다. 이때 state란 이 메소드 안에서만 유효한 값들을 의미.
constructor(props) {
  super(props);
}

🧱 componentWillMount

  • 컴포넌트가 화면에 나가나기 직전에 호출되는 API
componentWillMount(){
    console.log("componentWillMount");
}

❗ 이 API 에 대해선 별로 신경쓰지 않아도 된다. 원래는 주로 브라우저가 아닌 환경에서 (서버사이드)도 호출하는 용도로 사용했었는데, 이 API 가 더 이상 필요하지 않게 되어 리액트 v16.3 에서는 해당 API 가 deprecated 되었다.
➡ v16.3 이후부터는 UNSAFE_componentWillMount() 라는 이름으로 사용된다.
기존에 이 API 에서 하던 것들은 위에 있는 constructor 와 아래에서 다뤄볼 componentDidMount 에서 충분히 처리 할 수 있다.

🧱 render

  • 컴포넌트 렌더링을 담당
render(){
         ...
        }

🧱 componentDidMount

  • 컴포넌트가 화면에 나타나게 됐을 때 호출
  • 컴포넌트가 만들어지고 첫 렌더링을 다 마친 후 실행되는 메소드.
  • 컴포넌트가 브라우저에 나타난 시점에 어떠한 작업을 할지 명시해 주는것
    (API 요청, event 설정 등)
  • 이 안에서 다른 JavaScript 프레임워크를 연동하거나, setTimeout, setInterval 및 Ajax 처리 등을 넣는다.
componentDidMount() {
  // 외부 라이브러리 연동: D3, masonry, etc
  // 컴포넌트에서 필요한 데이터 요청: Ajax, GraphQL, etc
  // DOM 에 관련된 작업: 스크롤 설정, 크기 읽어오기 등
}

❗ 여기선 주로 D3, masonry 처럼 DOM 을 사용해야하는 외부 라이브러리 연동을 하거나, 해당 컴포넌트에서 필요로하는 데이터를 요청하기 위해 axios, fetch 등을 통하여 ajax 요청을 하거나, DOM 의 속성을 읽거나 직접 변경하는 작업을 진행.


🪐 컴포넌트 업데이트
컴포넌트 업데이트props의 변화, 그리고 state의 변화에 따라 결정된다.
업데이트가 되기 과 그리고 된 에 호출되는 API
= Updating

🧱 componentWillReceiveProps

  • 컴포넌트가 새로운 props 를 받게됐을 때 호출
  • 이 안에서는 주로, state 가 props 에 따라 변해야 하는 로직을 작성한다.
  • props의 변경에 따라 state를 업데이트 해야 할 때 사용하면 유용하다.
  • 이 안에서 this.setState() 를 하더라도 추가적으로 렌더링하지 않는다.
componentWillReceiveProps(nextProps) {
  // this.props 는 아직 바뀌지 않은 상태
}

❗ 새로 받게될 props 는 nextProps 로 조회 할 수 있으며, 이 때 this.props 를 조회하면 업데이트 되기 전의 API 이다. 이 API 또한 v16.3 부터 deprecate 된다. v16.3 부터는 UNSAFE_componentWillReceiveProps() 라는 이름으로 사용된다. 그리고, 이 기능은 상황에 따라 새로운 API getDerivedStateFromProps 로 대체 될 수도 있다.

🧱 static getDerivedStateFromProps()

  • v16.3 이후에 만들어진 라이프사이클 API.
  • props 로 받아온 값을, state 로 동기화 하는 작업을 해줘야 하는 경우에 사용된다.
static getDerivedStateFromProps(nextProps, prevState) {
  // 여기서는 setState 를 하는 것이 아니라
  // 특정 props 가 바뀔 때 설정하고 설정하고 싶은 state 값을 리턴하는 형태로 사용된다.

  /*
  if (nextProps.value !== prevState.value) {
    return { 
         value: nextProps.value 
    };
  }
  return null; // null 을 리턴하면 따로 업데이트 할 것은 없다라는 의미
  */
}

🧱 shouldComponentUpdate

  • props 혹은 state 가 변경 되었을 때, 리렌더링을 할지 말지 정하는 메소드
    컴포넌트를 최적화하는 작업에서 매우 유용하게 사용 (❗ 성능 최적화와 관련된 함수)
    ➡ Virtual DOM 에 리렌더링 하는것도, 불필요할경우엔 방지하기 위해서 shouldComponentUpdate 를 작성
  • 이 함수는 기본적으로 true 를 반환
    ❗ 따로 작성을 해주어서 조건에 따라 false 를 반환하면 해당 조건에는 render 함수를 호출하지 않는다.
    (🎃 특정 조건에 따라 rendering을 막아줄 수 있는 함수)
shouldComponentUpdate(nextProps, nextState) {
  // return false 하면 업데이트를 안함
  return this.props.checked !== nextProps.checked
  return true;  // 기본값은 true
}

🧱 componentWillUpdate

  • shouldComponentUpdate 에서 true 를 반환했을때만 호출된다.
    ➡ 만약에 false 를 반환했었다면 이 함수는 호출되지 않는다.
  • 주로 애니메이션 효과를 초기화하거나, 이벤트 리스너를 없애는 작업을 한다.
  • 이 함수가 호출되고난 다음에는, render() 가 호출된다(컴포넌트가 업데이트 되기 전에 실행)
    ❗ 이 메소드 안에서 this.setState() 를 사용할 경우 무한루프에 빠지므로 주의해야 한다.
componentWillUpdate(nextProps, nextState) {

}

이 API 또한 v16.3 이후 deprecate 됩니다. 기존의 기능은 getSnapshotBeforeUpdate 로 대체 될 수 있습니다.

🧱 getSnapshotBeforeUpdate()

  • 렌더링 후 결과물이 브라우저에 반영되기 바로 직전에 호출되는 함수
  • 스크롤의 위치, 해당 돔의 크기 등을 가져오는 작업을 하고 싶을 때 사용
  • 이 API 가 발생하는 시점은 다음과 같다.
render()
getSnapshotBeforeUpdate()
//실제 DOM 에 변화 발생
componentDidUpdate
//이 API를 통해서, DOM 변화가 일어나기 직전의 DOM 상태를 가져오고, 여기서 리턴하는 값은 componentDidUpdate 에서 3번째 파라미터로 받아올 수 있게 됩니다.

예시코드:
getSnapshotBeforeUpdate(prevProps, prevState) {
    // DOM 업데이트가 일어나기 직전의 시점.
    // 새 데이터가 상단에 추가되어도 스크롤바를 유지한다고 가정.
    // scrollHeight 는 전 후를 비교해서 스크롤 위치를 설정하기 위함이고,
    // scrollTop 은, 이 기능이 크롬에 이미 구현이 되어있는데, 
    // 이미 구현이 되어있다면 처리하지 않도록 하기 위함.
    if (prevState.array !== this.state.array) {
      const {
        scrollTop, scrollHeight
      } = this.list;

      // 여기서 반환 하는 값은 componentDidMount 에서 snapshot 값으로 받아올 수 있습니다.
      return {
        scrollTop, scrollHeight
      };
    }
  }


componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot) {
      const { scrollTop } = this.list;
      if (scrollTop !== snapshot.scrollTop) return; // 기능이 이미 구현되어있다면 처리하지 않습니다.
      const diff = this.list.scrollHeight - snapshot.scrollHeight;
      this.list.scrollTop += diff;
    }
  }
전체코드는 https://codesandbox.io/s/484zvr87ow 에서 확인 하실 수 있습니다.

🧱 componentDidUpdate()

  • 컴포넌트가 리렌더링을 마친 후 실행 (컴포넌트에서 render() 를 호출하고난 다음에 발생)
  • 이 시점에선 this.props 와 this.state 가 바뀌어있다.
  • 파라미터를 통해 이전의 값인 prevProps 와 prevState 를 조회 할 수 있다.
  • getSnapshotBeforeUpdate 에서 반환한 snapshot 값은 세번째 값으로 받아온다
componentDidUpdate(prevProps, prevState, snapshot) {

}

🪐 컴포넌트 제거
컴포넌트가 더 이상 필요하지 않게 되면 단 하나의 API 가 호출된다
= Unmounting

🧱 componentWillUnmount()

  • 컴포넌트가 DOM 에서 사라지는 과정에서 호출되는 메소드
  • 이전에 설정한 eventListener를 없애주는 작업을 담당.
componentWillUnmount() {
  // 이벤트, setTimeout, 외부 라이브러리 인스턴스 제거
}

❗ 주로 등록했던 이벤트를 제거하고, 만약에 setTimeout 을 걸은것이 있다면 clearTimeout 을 통하여 제거한다. 추가적으로, 외부 라이브러리를 사용한게 있고 해당 라이브러리에 dispose 기능이 있다면 여기서 호출할 수 있다.


🐳 실습

import React, { Component } from 'react';

class Counter extends Component {

  // state 설정 방법 (1) ➡ constructor 함수 사용
  constructor(props) {
    super(props);
    // console.log('constructor'); 첫 번째 호출
  }
  
 // state 설정 방법 (2)
 // state = {
 //   number: 0
 // }

  componentDidMount() {
    // console.log('componentDidMount'); 두 번째 호출
  }

  
  // 🐙 특정 조건에 따라 rendering을 막아줄 수 있는 함수
  shouldComponentUpdate(nextProps, nextState) {
    // 5 의 배수라면 리렌더링 하지 않음 (5의 배수가 아니면 rendering되어 값이 나타남)
    console.log('shouldComponentUpdate');
    if (nextState.number % 5 === 0) return false;
    return true;
  }

  
  componentDidUpdate(prevProps, prevState, snapshot) {
    if (this.props.value !== prevProps.value) {
        console.log('value 값이 바뀌었다!', this.props.value);
      // 출력결과:  value 값이 바뀌었다! 2
    }
  }
    
    
 componentWillUnmount() {
        console.log('componentWillUnmount');
     
  }  


  handleIncrease = () => {
    const { number } = this.state;
    this.setState({
      number: number + 1
    });
  }

  handleDecrease = () => {
    this.setState(
      ({ number }) => ({
        number: number - 1
      })
    );
  }

  render() {
    console.log('render');
    return (
      <div>
        <h1>카운터</h1>
        <div>: {this.state.number}</div>
        <button onClick={this.handleIncrease}>+</button>
        <button onClick={this.handleDecrease}>-</button>
      </div>
    );
  }
}

export default Counter;

🚀 컴포넌트에 에러 발생

render 함수에서 에러가 발생한다면, 리액트 앱이 크래쉬 되어버린다.
그러한 상황에 유용하게 사용 할 수 있는 API componentDidCatch !

componentDidCatch(error, info) {
  this.setState({
    error: true
  });
}

에러가 발생하면 이런식으로 componentDidCatch 가 실행되게 하고,
state.error 를 true 로 설정하게 하고,
render 함수쪽에서 이에 따라 에러를 띄워준다.

❗ 주의
컴포넌트 자신의 render 함수에서 에러가 발생해버리는것은 잡아낼 수는 없지만,
그 대신에 컴포넌트의 자식 컴포넌트 내부에서 발생하는 에러들을 잡아낼 수 있다.

profile
boma91@gmail.com

0개의 댓글