ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React.js] 리액트 라이프 사이클(React Lifecycle)
    Front-end/React.js 2020. 6. 21. 17:51
    반응형

     

    포스팅 계기

     

    당연한 말일 수도 있지만, 안드로이드, 웹 프레임워크 등 모두 life cycle을 잘 이해해야 탄탄한 개발을 할 수 있다. 이제 리액트를 시작하는 단계이기 때문에, life cycle부터 공부해보기로 했다. 라이프 사이클을 이해하기 위해서 React 인스턴스 property(propsstate) 개념이 선행될 필요가 있다. 아직 두 개념에 대한 이해가 부족하다면 위의 링크를 통해 공부하길 추천한다.


    Life cycle (ver 16.4^)

     

    기존 Life cycle과는 다소 차이가 있다. 하지만 React blog에서 밝히는 변경이유는 아래와 같다.

     - 초기 렌더링 작업을 제어하는 방법이 너무 많아서 혼란이 됨.

     - Error Handling의 중단 작업이 고려되지 않아서 memory leak을 야기할 수 있음.

     - React 커뮤니티에서도 혼란이 됨 (...?리린이라 잘 공감하지 못함)

     

    이제는 현재 사용중인 Life cycle을 다이어그램을 통해 top-down 방식으로 보도록 해보자.

    주황색 lifecycle은 잘 사용하지 않는다.

    마운트 

     

    아래 메서드들은 컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출된다.

     

    - constructor()

    • this.state에 객체를 할당하여 local state를 초기화
    • 인스턴스에 이벤트 처리 메서드(onClick...)를 바인딩
    constructor(props) {
      super(props); // 선언해주지 않으면 버그로 이어질 수 있음.
      this.state = { counter: 0 }; // state 정의
      this.handleClick = this.handleClick.bind(this); // 이벤트 처리 메서드 바인딩
    }

    - getDerivedStateFromProps()

    • props에 의해 state를 갱신하기 위한 객체 반환
    • 권장) props의 변화에 따른 이벤트가 필요하다면, componentDidUpdate() 
    • 권장) props의 변화에 따른 데이터 계산이 필요하다면, Memoziation Helper
    • 권장) props의 변화에 따른 일부 state 값의 재설정이 필요하다면, 완전 제어 컴포넌트 또는 key를 사용하는 완전 비제어 컴포넌트

    - render()

    • 예상되는 output: React 엘리먼트, 배열과 Fragement(여러 엘리먼트), Protal(DOM 하위 트리에 자식 엘리먼트 렌더링), 문자열 &. 숫자(DOM 상의 텍스트 노드), Boolean || null (아무것도 렌더링 하지 않는다)
    • 순수함수여야 한다. (호출될 때마다 동일한 output, state 변경 x)
    • 권장) 브라우저와 상호작용하는 작업이 필요하다면, componentDidMount()

      ** shouldComponentUpate()가 false일시 render()는 호출되지 않는다.

     

    - componentDidMount()

    • 컴포넌트가 마운트된 직후, 즉 트리에 삽입된 직후 호출 됨.
    • DOM 노드가 있어야하는 초기화 작업을 함.
    • 비권장) setState() -> render()를 두번 호출하게 되므로 퍼포먼스 문제를 야기할 수 있음. -> constructor() 사용

    ** Depreated: componentWillMount()

     


    업데이트

     

    props 또는 state가 변경되면 갱신이 발생한다. 아래 메서드들은 컴포넌트가 다시 렌더링될 때 순서대로 호출된다.

     

    - getDerivedStateFromProps()

    - shouldComponentUpdate(nextProps, nextState)

    • props 또는 state가 새로운 값으로 갱신되어서 렌더링이 발생하기 직전 호출. (default: true)
    • (nextProps, nextState), (this.props, this.state)의 비교를 통해 성능 최적화를 위해 사용할 수 있지만, JSON.stringify()를 통한 로직은 권장하지 않음. -> 성능 비효율
    • 권장) pureComponent로 사용

     

    - render()

    - getSnapshotBeforeUpdate(prevProps, prevState)

    • 가장 마지막으로 렌더링된 결과가 DOM 등에 반영되었을 때 호출 됨.
    • 컴포넌트가 DOM으로부터 스크롤 위치와 같은 정보를 변경되기 전에 사용할 수 있음
    • ouput은 componentDidUpdate()에 전달 됨.
    getSnapshotBeforeUpdate(prevProps, prevState) {
        // Are we adding new items to the list?
        // Capture the scroll position so we can adjust scroll later.
        if (prevProps.list.length < this.props.list.length) {
          const list = this.listRef.current;
          return list.scrollHeight - list.scrollTop;
        }
        return null;
      }

     

    - componentDidUpdate(prevProps, prevState, snapshot)

    • 갱신이 일어난 직후에 호출 됨.
    • 컴포넌트가 갱신되었을 때 DOM을 조작하기 위해 활용 (i.e props를 비교하여 네트워크 요청)
    • getSnapshotBeforeUpdate의 snapshot이 있을 때 핸들링
    componentDidUpdate(prevProps, prevState, snapshot) {
      // 전형적인 사용 사례 (props 비교를 잊지 마세요)
      if (this.props.userID !== prevProps.userID) {
        this.fetchData(this.props.userID);
      }
      // getSnapshotBeforeUpdate로부터의 snapshot이 있을 때 사용
      // 새로운 list item이 추가되면 scroll 위치를 변경함
      if (snapshot !== null) {
        const list = this.listRef.current;
        list.scrollTop = list.scrollHeight - snapshot;
      }
    }

    컴포넌트가 갱신되었을 때 DOM을 조작하기 위하여 이 메서드를 활용

     

    ** Depreated: componentWillUpdate(), componentWillReceiveProps()

     


    마운트 해제

     

    아래 메서드는 컴포넌트가 DOM 상에서 제거될 때에 호출된다.

     

    - componentWillUnmount()

    • 컴포넌트가 마운트 해제되어 제거되기 직전에 호출 됨.
    • 타이머 제거, 네트워크 요청 취소 등 componentDidMount()에서 생성된 것들을 해제
    • 주의) 다시 렌더링되지 않으므로 setState()를 호출하면 안됨

     


    에러

     

    아래 메서드들은 자식 컴포넌트를 렌더링하거나, 자식 컴포넌트가 생명주기 메서드를 호출하거나, 또는 자식 컴포넌트가 생성자 메서드를 호출하는 과정에서 오류가 발생했을 때에 호출된다.

     

    - getDerivedStateFromError(error)

    • Child 컴포넌트에서 오류가 발생했을 때 호출 됨.
    • 매개변수로 오류를 전달받고, 갱신된 state 값을 반환해야 함.
    • render 단계에서 호출되므로 side-effect를 사용하면 안됨. -> componentDidCatch()에서 사용
    • error를 통한 대체된 UI를 렌더링.
    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        // state를 갱신하여 다음 렌더링에서 대체 UI를 표시합니다.
        return { hasError: true };
      }
    
      render() {
        if (this.state.hasError) {
          // 별도로 작성한 대체 UI를 렌더링할 수도 있습니다.
          return <h1>Something went wrong.</h1>;
        }
    
        return this.props.children;
      }
    }

    - componentDidCatch(error, info)

    • Child 컴포넌트에서 오류가 발생했을 때 호출 됨.
    • info -> 어떤 컴포넌트가 오류를 발생시켰는지에 대한 정보(componentStack 키를 가진 객체)
    • commit 단계에서 호출되므로 오류 로그 기록 등을 위해 사용 함.
    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        // state를 갱신하여 다음 렌더링에서 대체 UI를 표시합니다.
        return { hasError: true };
      }
    
      componentDidCatch(error, info) {
        // Example "componentStack":
        //   in ComponentThatThrows (created by App)
        //   in ErrorBoundary (created by App)
        //   in div (created by App)
        //   in App
        logComponentStackToMyService(info.componentStack);
      }
    
      render() {
        if (this.state.hasError) {
          // 별도로 작성한 대체 UI를 렌더링할 수도 있습니다.
          return <h1>Something went wrong.</h1>;
        }
    
        return this.props.children;
      }
    }

     


    참고

    https://velopert.com/1130
    https://www.zerocho.com/category/React/post/579b5ec26958781500ed9955
    https://velog.io/@kyusung/리액트-교과서-컴포넌트와-라이프사이클-이벤트
    https://ko.reactjs.org/docs/react-component.html

     

    반응형

    'Front-end > React.js' 카테고리의 다른 글

    [React.js] 리액트 훅스 (React Hooks)  (0) 2020.07.12

    댓글

Designed by Tistory.