리액트 - LifeCycle API

velopert님의 블로그에서 리액트 기초 과정을 보고 리액트를 알아가는 시간을 가졌으며,
해당 포스팅은 위 과정을 이해한대로 정리한 것이다.
(https://velopert.com/reactjs-tutorials)

LifeCycle API : 컴포넌트가 브라우저에서 나타날 때, 사라질 때, 업데이트 될 때 호출되는 API

컴포넌트의 초기 생성

컴포넌트가 브라우저에 나타나기 전, 후에 호출되는 API들은 아래와 같다.

  • constructor
    : 컴포넌트가 새로 만들어질 때마다 호출되는 컴포넌트 생성자 함수
  • componentWillMount
    : 컴포넌트가 화면에 나가기 직전에 호출되는 API
    v16.3 이후부터는 UNSAFE_componentWillMount()라는 이름으로 사용
  • componentDidMount
    : 화면에 컴포넌트가 나타나게 되었을 때 호출
    주로 D3, masonry와 같이 DOM을 사용해야하는 외부 라이브러리 연동,
    필요한 데이터 요청을 위해 axios, fetch등을 통해 ajax 요청,
    DOM의 속성을 읽거나 직접 변경하는 작업 진행

컴포넌트 업데이트

컴포넌트의 업데이트는 props와 state의 변화에 따라 결정된다.

  • componentWillReceiveProps
componentWillReceiveProps(nextProps){
}

컴포넌트가 새로운 props를 받게 됐을 때 호출, 주로 state가 props에 따라 변해야 하는 로직 작성
새로 받게될 propsnextProps로 조회, 이 때 this.props업데이트 되기 전 API이다.
v16.3부터는 UNSAFE_componentWillReceiveProps()라는 이름으로 사용

  • static getDerivedStateFromProps()
    v16.3 이후에 만들어진 LifeCycle API, props로 받아온 값을 state로 동기화 하는 작업을 할 때 사용
static getDerivedStateFromProps(nextProps, prevState) {
  // 여기서는 setState 를 하는 것이 아니라
  // 특정 props 가 바뀔 때 설정하고 설정하고 싶은 state 값을 리턴하는 형태로
  // 사용됩니다.
  /*
  if (nextProps.value !== prevState.value) {
    return { value: nextProps.value };
  }
  return null; // null 을 리턴하면 따로 업데이트 할 것은 없다라는 의미
  */
}
  • shouldComponentUpdate
    리액트의 특징인 변화가 발생한 부분만 감지하여 업데이트하기 위해서는 virtual DOM에 그리는 작업이 필요하다. 하지만 이 작업마저도 불필요할 경우에는 방지하기 위해 shouldComponentUpdate를 작성한다.
shouldComponentUpdate(nextProps, nextState) {
  // return false 하면 업데이트를 안함
  // return this.props.checked !== nextProps.checked
  return true;
}

조건에 따라 false를 반환하면 해당 조건에 render 함수를 호출(렌더링)하지 않는다.

  • componentWillUpdate
    shouldComponentUpdate에서 true를 반환했을 때 호출
    주로 애니메이션 효과를 초기화하거나, 이벤트 리스너를 없애는 작업을 한다.
    이 함수가 호출되고 난 후 render() 호출
    v16.3 이후로 deprecate되어 getSnapshotBeforeUpdate로 대체 될 수 있다.
componentWillUpdate(nextProps, nextState) {

}
  • getSnapshotBeforeUpdate
  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;
    }
  }

DOM 변화가 일어나기 직전의 DOM 상태를 가져오고, 리턴 값은 componentDidUpdate에서 3번째 파라미터로 받아올 수 있다.

  • componentDidUpdate
componentDidUpdate(prevProps, prevState, snapshot) {

}

render()를 호출하고난 다음에 발생, 이 시점에서는 this.props와 this.state가 바뀐다.
파라미터를 통해 이전의 값인 prevProps와 prevState를 조회할 수 있다.
getSnapshotBeforeUpdate에서 반환한 snapshot값은 세번째 값으로 받아온다.

컴포넌트 제거

컴포넌트가 더 이상 필요하지 않을 경우, 호출되는 단 하나의 API

  • componentDidUpdate
    주로 등록했던 이벤트를 제거하고 setTimeout을 clearTimeout을 통해 제거, 외부 라이브러리를 사용하고 해당 라이브러리에 dispose 기능이 있다면 호출하는 것과 같은 일들을 함

적용하기

Counter.js를 아래와 같이 변경해보자.

class Counter extends Component {
  state = {
    number: 0
  }

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

  componentDidMount() {
    console.log('componentDidMount');
  }

  shouldComponentUpdate(nextProps, nextState) {
    // 5 의 배수라면 리렌더링 하지 않음
    console.log('shouldComponentUpdate');
    if (nextState.number % 5 === 0) return false;
    return true;
  }

  componentWillUpdate(nextProps, nextState) {
    console.log('componentWillUpdate');
  }
  
  componentDidUpdate(prevProps, prevState) {
    console.log('componentDidUpdate');
  }
  

  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>
    );
  }
}


console창을 통해 5의 배수일때 컴포넌트가 리렌더링 되지 않는 것을 확인 할 수 있다.

좋은 웹페이지 즐겨찾기