[ReactJS] ReactJS 의 생명주기

ReactJS 의 생명주기

거의 대부분의 언어에 메모리를 할당하는 과정이 있듯 ReactJS 에도 생명 주기(Life cycle)라는 것이 있다. 필수적으로 알고 있어야 하는 것은 아니지만, 각각의 훅에서 어떠한 일이 벌어지는 지 정도는 알고 있어야 어느 훅에서 API 를 패치해와야하는지, 어떤 훅에서 DOM 자체의 이벤트 바인딩을 해제 해줘야 하는지에 대한 가이드가 생길 수 있다. 또한 ReactJS 의 경우 특정 훅을 이용하여 컴포넌트의 최적화를 할 때 역시 유용하게 사용할 수 있으므로, 자주 사용하는 생명 주기의 경우는 알고 있는 것을 추천한다.

먼저 Javascript 의 모든 생명 주기는 비슷한 흐름을 가지고 있다.

컴포넌트가 마운트가 되기 전의 훅들이 존재하며 마운트가 된 후에는 데이터의 변화에 따라 컴포넌트가 다시 랜더링 된다. 그 후, 컴포넌트의 역할이 끝남에 따라 컴포넌트가 사라지게 된다. 필자는 개인적으로는 각 생명주기에서 브레이크 포인트를 찍어서 하나 하나 확인하는 것을 추천한다.

constructor

React 의 경우 하나 하나의 컴포넌트가 class 기반으로 되어 있다. Class 에는 기본적으로 constructor 가 존재하듯 마찬가지로 React 의 컴포넌트에서도 constructor 가 존재한다. 이 훅은 우리가 만든 컴포넌트가 처음 브라우저에 생성될 떄 가장 최초로 실행되는 함수로서, 보통 컴포넌트가 가질 state 를 초기화할 때 사용한다.

class App extends Component {
  constructor () {
    this.state = {
      value: 0,
    }
  }
}

getDerivedStateFromProps

Props 로 받은 데이터를 state 에 동기화를 시키려할 때 사용하는 훅으로서, Props 가 변화할 때마다 실행된다. 만약 state 의 값을 변경할 필요가 없다면 null 값을 반환해주면 된다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };

  static getDerivedStateFromProps (nextProps, nextState) {
    if (nextProps !== nextState) {
        return {
          value: nextProps.value
        };
    }
    return null;
  }
}

render

render 함수는 어떠한 DOM 을 만들지, 내부 태그에는 어떠한 값이 들어갈지에 대한 것을 결정하는 함수이다. React 컴포넌트에 Stateless Component 와 Stateful Component, 두 가지로 나뉘는데 두 종류의 컴포넌트 모두 해당 훅은 필수적으로 가지고 있어야 할 정도로 React 컴포넌트에서는 필수적인 메소드이다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };

  static getDerivedStateFromProps (nextProps, nextState) {
    if (nextProps !== nextState) {
        return {
          value: nextProps.value
        };
    }
    return null;
  }
  
  render () {
      return (
          <div>
            <p>MyComponent1 :{this.props.value}</p>
            <p>MyComponent1 :{this.state.value}</p>
          </div>
      )
    }
}

componentDidMount

우리가 정의한 컴포넌트가 브라우저상에 DOM 으로 나타나게 되면 실행되는 함수이다. 이 훅에서는 D3, Chart 같은 DOM 에 해당 라이브러리에서 제공하는 UI 를 그려야할 때 그 요청을 실행하는 훅이다. 이 뿐만 아니고 API 서버에 데이터를 호출하는 함수를 해당 훅에서 실행시켜주며, 어떠한 DOM 에 이벤트를 바인딩할 때 역시 해당 훅에서 실행된다. 필자의 경우 이 훅은 인피니티 스크롤을 만들 때 사용하였다. window 자체에 onScroll 이벤트를 해당 훅에서 바인딩을 해주어 스크롤 이벤트가 발생할 때 일정 조건을 충족할 경우 API 의 데이터를 받아오도록 하였다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };
  componentDidMount () {
      window.addEventListener("scroll", function () {
        // Do something
      });
  }
  render () {
      return (
          <div>
            <p>MyComponent1 :{this.props.value}</p>
            <p>MyComponent1 :{this.state.value}</p>
          </div>
      )
    }
}

shouldComponentUpdate

컴포넌트가 업데이트 되는 성능을 최적화할 때 사용하는 훅으로서, 개인적으로는 생명 주기 중에 중요한 훅 중 하나라고 생각한다. 부모 컴포넌트가 랜더함수가 실행되면 자식 컴포넌트도 랜더함수가 실행된다. 부모 컴포넌트가 업데이트 되더라도 일부 자식 컴포넌트의 props 는 업데이트 되지 않는 경우가 있다. DOM 에 그리는 것이 아니라 vitual dom 에 그리는 것이라 화면에는 따로 보여지지 않지만 props가 업데이트 되지 않아도 모든 자식 컴포넌트들이 render 함수를 실행한다. 이럴 때 해당 훅을 이용하여 랜더링 함수 실행되지 않도록 막을 수 있다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };
  shouldComponentUpdate (nextProps, nextState) {
    if (nextProps === nextState)
      return false;
    } else {
      return true;    
    }
  }
  render () {
      return (
          <div>
            <p>MyComponent1 :{this.props.value}</p>
            <p>MyComponent1 :{this.state.value}</p>
          </div>
      )
    }
}

getSnapshotBeforeUpdate

해당 훅은 랜더 함수 실행 후, 랜더링한 결과물이 브라우저에 반영되기 바로 직전에 실행되는 함수이다. 랜더링 후 업데이트 전 스크롤의 위치 혹은 해당 돔의 크기를 가져올때 해당 훅에서 사용한다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };
  getSnapshotBeforeUpdate (prevProps, prevState) {
    // Do something
  }
  render () {
      return (
          <div>
            <p>MyComponent1 :{this.props.value}</p>
            <p>MyComponent1 :{this.state.value}</p>
          </div>
      )
    }
}

componentDidUpdate

브라우저에 랜더링된 직후 실행되는 함수이다. 이전의 페이지와 현재의 페이지가 서로 다를 경우 해당 훅에서 특정 작업을 할 수 있다. 해당 훅에서는 랜더링 된 후에 실행되는 함수이므로 DOM 을 제어하는 등의 일 또한 추가해도 무방하다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };
  componentDidUpdate (prevProps, prevState, snapshot) {
    // Do something
  }
  render () {
      return (
          <div>
            <p>MyComponent1 :{this.props.value}</p>
            <p>MyComponent1 :{this.state.value}</p>
          </div>
      )
    }
}

componentWillUnmount

componentDidMount 훅에서 DOM 에 이벤트를 바인딩했을 때 그 이벤트를 해제시켜줄때 해당 훅에서 시켜주면 된다. 위의 componentDidMount 훅에서 필자처럼 window 에 scroll 이벤트를 바인딩했다면 해당 훅에서 제거하면 된다.

class ChildrenComponent extends Component {
  state = {
    value: 0,
  };
  componentWillUnmount () {
    widnow.removeEventListener("scroll", function () {
      // Do something
    })
  }
  render () {
      return (
          <div>
            <p>MyComponent1 :{this.props.value}</p>
            <p>MyComponent1 :{this.state.value}</p>
          </div>
      )
    }
}

Comments