[React] LifeCycle API

한지원·2021년 10월 15일
0

컴포넌트가 브라우저 상에 나타날 때, 업데이트 될 때, 사라질 때 이 중간 과정에서 어떤 작업을 수행하고자 할 때 LifeCycle API를 사용한다.

LifeCycle API란

Mounting

mounting이란 컴포넌트가 브라우저 상에 나타나는 것이다.

mounting 함수들
constructor: 생성자 함수, 컴포넌트가 브라우저에 처음 나타날 때 가장 먼저 실행되는 함수이다. 여기에서는 보통 컴포넌트가 가지는 state나 미리 수행해야 하는 작업을 넣어준다.

getDerivedStateFromProps: props로 받은 값을 state로 동기화하고자 할 때 사용된다. (updating과정에서도 props가 바뀔 때 이 함수가 실행된다.)

render: 어떤 돔을 만들게 되고 내부의 태그는 어떤 값을 전달해줄지 정의.

componentDidMount: 주로 외부 라이브러리를 사용할 때 이 함수에서 특정 돔에 차트를 그려달라고 요청할 수 있다. 또는 ajax요청을 처리할 때 이 함수에서 처리한다. 또 컴포넌트가 나타나도 몇초 후에 이벤트를 하고싶다와 같은 이벤트를 처리할 때에도 사용된다. 즉 컴포넌트가 브라우저에 나타나고 어떤 시점에 작업을 주고자 할 때 명시해주는 함수이다.

Updating

컴포넌트의 props나 state가 바뀌었을 때를 나타낸다.

getDerivedStateFromProps: Mounting에서 쓰이는 함수와 같다. props의 값을 state에 동기화 시킬 때 사용

shouldComponentUpdate: 컴포넌트가 업데이트되는 성능을 최적화시킬 때 사용한다. 컴포넌트는 기본적으로 부모 컴포넌트가 업데이트될 때 자식컴포넌트도 모두 렌더함수가 작동한다. 그러나 가상 돔에서 공부했다시피 컴포넌트가 변경되는 영역만 리렌더되는 것이 렌더링 과정이 빠르다는 장점이 있는 건데 부모 컴포넌트가 렌더될 때 자식도 렌더되면 눈에는 안보여도 리렌더가 되어 가상 돔에 적용되고 있다는 것이다. 부모 컴포넌트의 변화를 자식 컴포넌트에도 적용해야할 때는 이것이 필요한 기능이고 장점이지만 그럴 필요가 없을 때는 shouldComponentUpdate 를 사용해서 필요 없는 가상돔의 업데이트를 막을 수 있다.
이 함수는 true나 false값을 반환할 수 있다. true값을 반환하면 렌더링 프로세스를 거치는 것이고 false값을 반환하면 리렌더 과정을 거치지 않고 props나 state의 변화를 반영하지 않게 된다.
shouldComponentUpdate 함수는 가상 돔에도 업데이트를 적용할지 말지를 결정해주는 역할을 한다.

getSnapshotBeforeUpdate: 앞의 shouldComponentUpdate함수와 render함수를 거친 후에 실행되는 함수이다. 렌더링 이후에 그 결과물이 브라우저에 반영되기 바로 직전에 호출되는 함수이다. 렌더링 이후에 스크롤의 위치나 해당 돔의 크기를 사전에 가져오고자 할 때 사용하는 함수이다.

componentDidUpdate: 위의 작업들을 마치고 컴포넌트가 업데이트 되었을 때 호출되는 함수이다. state가 바뀌었을 때 이전의 상태와 지금의 상태가 달라졌을 때 (예를들어 페이지가 달라졌을 때) 작업을 설정할 수 있다.

Unmounting

컴포넌트가 브라우저에서 사라질 때를 나타낸다.
componentWillUnmount: 컴포넌트가 사라지는 과정에서 호출되는 함수이다. 앞의 mount과정에서 호출 되는 함수인 componentDidMount함수에서는 이벤트를 리스닝할 수 있다고 했는데 componentWillUnmount 에서는 앞에서 설정한 이벤트 리스너를 없애주는 작업을 하면 된다.

LifeCycle API 코드상에서 다뤄보기

constructor

App.js

import React, { Component } from 'react';

class App extends Component {
  constructor(props){ //1
    super(props); //2
    console.log('constructor');//3
  }
  render() {
    return(
      <div>안녕하세요</div>
    );
  }
}
  1. 컨스트럭터 함수 생성 후 props를 파라미터로 받아온다.
  2. 컴포넌트를 만들게 될 때 react에서 불러온 Component를 extends하고 그것을 상속하는 것인데 위에서 상속받은 Component를 먼저 호출하고 우리가 하고싶은 작업을 해준다.
  3. 해당 코드를 실행시켜보면 콘솔 창에 'constructor'라는 문자열이 출력된다.

componentDidMount

App.js

import React, { Component } from 'react';

class App extends Component {
  constructor(props){ //1
    super(props); //2
    console.log('constructor'); //3
  }
  
  componentDidMount() {
  	console.log('componentDidMount'); //4
  }
  render() {
    return(
      <div>안녕하세요</div>
    );
  }
}
  1. 실행시키면 위의 콘솔창에 'constructor'가 출력되고 그 다음에 'componentDidMount'가 출력된다.
    (componentDidMount를 맨 위에 위치시켜도 'constructor'가 먼저 출력됨)

componentDidMount 를 이용해서 돔 조작하기
App.js

import React, { Component } from 'react';

class App extends Component {
  constructor(props){ //1
    super(props); //2
    console.log('constructor'); //3
  }
  
  componentDidMount() {
  	console.log('componentDidMount'); //4
    console.log('this.myDiv.getBoundingClientRect()'); //6
  }
  render() {
    return(
      <div ref={ref => this.myDiv = ref}>안녕하세요</div> //5
    );
  }
}

ref란 DOM에 id를 붙이는것과 비슷하다. 돔에 대한 레퍼런스를 바로 가져올 수 있는 것.

  1. ref= //ref는
    {ref=>this.myDiv = ref} //ref를 파라미터로 가져오는 함수를 작성해서 설정해준다.

  2. 실행시킨 후 개발자 도구를 보면 myDiv의 각 요소들의 위치, 크기와 같은 값들을 볼 수 있다.

getDerivedStateFromProps()

props를 state로 동기화시키고자 할 때 객체를 return해주면 된다. 그러면 반환된 그 객체의 값이 state로 된다.
MyComponent.js

import React, { Component } from 'react';

class MyComponent extends Component {
  state = {
  	value: 0
  };

  static getDerivedStateFromProps(nextProps, prevState) { //7
  if (prevState.value !== nextProps.value {
        return {
          value: nextProps.value //8
        }
  }
  return null; //9
}
  render() {
    return (
      <div>
      	<p>props: {this.props.value}</p>
  		<p>state: {this.state.value}</p>
      </div>
	);
  }
}
export default MyComponent;

App.js

import React, { Component } from 'react';
import MyComponent from './MyComponent';
class App extends Component {
  state = {
    counter: 1,
  }
  constructor(props){
    super(props);
    console.log('constructor');
  }
  
  componentDidMount() {
  	console.log('componentDidMount');
    	console.log('this.myDiv.getBoundingClientRect()');
  }
  handleClick = () => { //10
    this.setState({
      counter: this.state.counter + 1
    });
  }
  render() {
    return(
      <div >
        <MyComponent value={this.state.counter}/>
		<button onClick={this.handleClick}>Click Me</button> //10
	  </div>
    );
  }
}
  1. getDerivedStateFromProps 는 static으로 생성해줘야한다. 파라미터로는 nextPropsprevState 인데, nextProps 는 다음으로 받아올 props값을 가져오고, prevState 는 변경되기 전인 현재의 상태를 가져오는 것이다.

  2. prevState와 NextProps의 값이 다를 때 nextProps의 value값으로 value를 셋팅해준다.

  3. 만약 변경사항이 없으면 그냥 비워두거나 null을 리턴해주면 된다.

  4. props값에 변화를 줬을 때 state가 어떻게 변하는지 알아보기 위해서 추가해준 코드
    버튼을 누르면 props값이 바뀌고 동시에 state값도 변하는 것을 알 수 있다.

shouldComponentUpdate

shouldComponentUpdate를 따로 구현하지 않는다면 true를 리턴하는 해당 함수가 디폴트로 설정된다. 특정 상황에 리렌더를 하지 않도록 하기 위해 false를 리턴할 조건을 구현한 뒤 리턴값을 false로 지정해주면 된다.
즉 업데이트를 막아주는 함수로 생각해주면 된다.

MyComponent.js

import React, { Component } from 'react';

class MyComponent extends Component {
  state = {
  	value: 0
  };

  static getDerivedStateFromProps(nextProps, prevState) { //7
    if (prevState.value !== nextProps.value {
      return {
        value: nextProps.value //8
      }
    }
    return null; //9
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.value === 10) return false;
    return true;
  } //11
  render() {
    return (
      <div>
      	<p>props: {this.props.value}</p>
  		<p>state: {this.state.value}</p>
      </div>
	);
  }
}
export default MyComponent;
  1. click Me 버튼을 클릭하면 props의 값이 1씩 증가하고 동시에 state도 props에 맞춰서 증가한다. 하지만 props가 10이 될 때 shouldComponentUpdate 함수에서 false를 리턴해주기 때문에 아무것도 변하지 않는 것으로 보여지고 한번 더 클릭했을 때 props와 state의 값이 11로변하는게 보여지게 된다.

getSnapshotBeforeUpdate()

컴포넌트가 업데이트 돼서 브라우저의 돔에 반영되기 바로 직전에 호출되는 함수이다.
업데이트 되기 바로 직전의 돔 상태를 리턴해준다.

componentDidUpdate

컴포넌트가 업데이트된 후에 호출되는 함수이다.
componentDidUpdate(prevProps, prevState, snapshot) {}
prevProps: 업데이트 되기 바로 직전의 props
MyComponent.js

import React, { Component } from 'react';

class MyComponent extends Component {
  state = {
  	value: 0
  };

  static getDerivedStateFromProps(nextProps, prevState) { //7
    if (prevState.value !== nextProps.value {
      return {
        value: nextProps.value //8
      }
    }
    return null; //9
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.value === 10) return false;
    return true;
  } //11

  componentDidUpdate(prevProps, prevState) {
    if(this.props.value !== prevProps.value) {
      colsole.log('value값이 바뀌었다!'); 
  } //12
  render() {
    return (
      <div>
      	<p>props: {this.props.value}</p>
  		<p>state: {this.state.value}</p>
      </div>
	);
  }
}
export default MyComponent;
  1. 버튼을 클릭하면 props의 값이 이전 상태와 달라지므로 콘솔창에 value값이 바뀌었다는 메시지를 출력시킨다.

componentWillUnmount

컴포넌트가 불필요해져서 사라질 때 나타나는 함수
MyComponent.js

import React, { Component } from 'react';

class MyComponent extends Component {
  state = {
  	value: 0
  };

  static getDerivedStateFromProps(nextProps, prevState) { //7
    if (prevState.value !== nextProps.value {
      return {
        value: nextProps.value //8
      }
    }
    return null; //9
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.value === 10) return false;
    return true;
  } //11

  componentDidUpdate(prevProps, prevState) {
    if(this.props.value !== prevProps.value) {
      colsole.log('value값이 바뀌었다!'); 
    }
  }//12
    
  componentWillUnmount() {
    console.log('good bye');
  } //14
 
  render() {
    return (
      <div>
      	<p>props: {this.props.value}</p>
  		<p>state: {this.state.value}</p>
      </div>
	);
  }
}
export default MyComponent;

App.js

import React, { Component } from 'react';
import MyComponent from './MyComponent';
class App extends Component {
  state = {
    counter: 1,
  }
  constructor(props){
    super(props);
    console.log('constructor');
  }
  
  componentDidMount() {
  	console.log('componentDidMount');
    	console.log('this.myDiv.getBoundingClientRect()');
  }
  handleClick = () => { //10
    this.setState({
      counter: this.state.counter + 1
    });
  }
  render() {
    return(
      <div>
      {this.state.counter < 10 && <MyComponent value={this.state.counter}/> } //13
		<button onClick={this.handleClick}>Click Me</button> //10
	  </div>
    );
  }
}
  1. counter의 값이 10보다 작을 때만 보여지게하는 코드를 넣어줘서 10을 넘어가면 사라지게 해준다. (componentWillUnmount를 실행시키기 위해)
    ( &&앞의 조건이 만족할 시 &&뒤를 보여지게 하는 문법임)

  2. 버튼을 10번 째 클릭해서 props와 state가 10이 될 때 props와 state는 화면에서 사라지게 되고 componentWillUnmount함수가 동작해서 콘솔창에 goodbye가 출력된다.

컴포넌트에 에러가 발생했을 때 쓰는 componentDIdCatch

만약 MyComponent.js 부분에서 에러가 난다고 하면 componentDidCatch함수는 에러가 나는 컴포넌트의 부모에서 호출해줘야한다.
MyComponent.js

import React, { Component } from 'react';

class MyComponent extends Component {
  state = {
  	value: 0
  };

  static getDerivedStateFromProps(nextProps, prevState) { //7
    if (prevState.value !== nextProps.value {
      return {
        value: nextProps.value //8
      }
    }
    return null; //9
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.value === 10) return false;
    return true;
  } //11

  componentDidUpdate(prevProps, prevState) {
    if(this.props.value !== prevProps.value) {
      colsole.log('value값이 바뀌었다!'); 
    }
  }//12
    
  componentWillUnmount() {
    console.log('good bye');
  } //14
 
  render() {
    return (
      <div>
        {this.props.missing.something }//15
        <p>props: {this.props.value}</p>
      	<p>props: {this.props.value}</p>
  		<p>state: {this.state.value}</p>
      </div>
	);
  }
}
export default MyComponent;
  1. missing은 정의하지 않은 오브젝트기 때문에 값이 undefined이다. 여기에서 .someting이라는 값을 찾고있기 때문에 render함수에서 오류가 발생했고 리액트는 이 상황에서 브라우저에 오류가 난다.

App.js

import React, { Component } from 'react';
import MyComponent from './MyComponent';
class App extends Component {
  state = {
    counter: 1,
  }
  constructor(props){
    super(props);
    console.log('constructor');
  }
  
  componentDidMount() {
  	console.log('componentDidMount');
    	console.log('this.myDiv.getBoundingClientRect()');
  }
  handleClick = () => { //10
    this.setState({
      counter: this.state.counter + 1
    });
  }
  componentDidCatch(error, info) { //16
  	console.log(error);
    console.log(info);
    
    this.setState({
      error: true,
    });
    //API를 통해 서버로 오류 내용 날려주기
    //17
  } 
  render() {
    if (this.state.error) {
      <div>에러발생</div>
    }
    return(
      <div>
      {this.state.counter < 10 && <MyComponent value={this.state.counter}/> } //13
		<button onClick={this.handleClick}>Click Me</button> //10
	  </div>
    );
  }
}
  1. error는 어떤 에러가 발생했는지를 알 수 있고 info는 어디에서 에러가 발생했는지 알려준다.

  2. api를 사용하면 에러 발생 시 오류가 발생했다고 메시지를 띄워준 뒤 서버로 에러의 종류를 보내줄 수 있다.

0개의 댓글

관련 채용 정보