Study/React

React 깊이 이해하기(2) - Life cycle

manon_e 2024. 11. 7. 17:32
반응형

 

 

 


 

리엑트의 생명주기 / 라이프 사이클

생명주기 메서드가 실행되는 시점은 크게 3가지로 나눌 수 있습니다.

 

mount : 컴포넌트가 마운팅(생성)되는 시점

update : 이미 생성된 컴포넌트의 내용이 변경(업데이트)되는 시점

unmount : 컴포넌트가 더 이상 존재하지 않는 시점

 

 

 

 

1. class component의 Life cycle

 

 

render()

render는 React Class Component의 유일한 필수값으로 쓰입니다.

이 함수는 컴포넌트가 UI를 렌더링하기 위해서 쓰이고, mount와 update 과정에서 일어납니다.

 

render() 함수는 항상 순수해야하며, 부수 효과가 없어야 한다는 것이 특징입니다. (no side effects)

순수해야 한다는 것은 입력값이 있으면 항상 똑같은 결과물을 반환한다는 뜻입니다.

따라서, render 내에서 state를 직업 업데이트하는 this.setState를 호출해서는 안됩니다.

 

 

 

componentDidMount() : 컴포넌트가 mount되고 준비되는 즉시 실행

render와 다르게 state 값을 변경하는 것이 가능합니다. this.setState를 호출했다면 state가 변경되고

그 즉시 다시한번 렌더링을 시도합니다.

 

 

 

componentDidUpdate() : 컴포넌트 업데이트가 일어난 이후 바로 실행

state나 props의 변화에 따라 DOM을 업데이트할 때 사용됩니다.

 

 

 

componentWillUnmount() : 컴포넌트가 언마운트되거나 더 이상 사용되지 않기 직전 호출

메모리 누수나 불필요한 작동을 막기 위한 clean-up 함수를 호출하는데에 주로 사용된다.

(이벤트를 지우기, API 호출 취소, setTimeout등의 타이머를 지우는 등의 작업)

 

 

 

shouldComponentUpdate() : state, props의 변경으로 컴포넌트가 리렌더링 되는 것을 막음

일반적으로 this.setState가 호출되면 컴포넌트는 리렌더링을 일으키지만,

이 메서드를 활용하면 컴포넌트에 영향을 받지 않는 변화에 대해 정의할 수 있습니다.

shouldComponentUpdate (nextProps: props, nextState: State) {
  // props의 title이나 input이 같지 않은 경우에는 컴포넌트를 업데이트 한다. (이외에는 업데이트 안함)
  return this.props.title !== nextProps.title || this.state.input !== nextState.input
}

class component에는 Component와 PureComponent 2가지 유형이 있는데,

이 둘의 차이점이 shouldComponentUpdate 메서드를 다루는데에 있습니다.

 

import React from 'react'

export class ReactPureComponent extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      count: 1,
    }
  }

  handleClick = () => {
    this.setState({ count: 1 })
  }

  render() {
    return <button onClick={this.handleClick}> + </button>
  }
}

위와 같이 버튼을 클릭하면 count state를 1 증가시키는 컴포넌트를 일반 Component로 만든다면 +1씩 계속 증가하지만,

PureComponent로 만든다면 state의 값이 업데이트되지 않아서 렌더링이 일어나지 않습니다.

PureComponent는 state 값에 대하여 얕은 비교를 수행해서 결과가 다를 때에만 렌더링을 수행하기 때문입니다.

이는 렌더링 최적화를 위한 메모이제이션(memoization)에서 활용할 수 있습니다.

 

 

 

static getDerivedStateFromProps() : render()를 호출하기 직전 호출

사라진 componentWillReceiveProps를 대체할 수 있는 메서드 입니다.

static으로 선언되어 있어 this에 접근할 수 없다는 것이 특징입니다.

여기서 반환하는 객체는 해당 객체의 내용이 모두 state로 들어가게 되고, null을 반환하면 아무것도 변경되지 않습니다.

이 메서드는 모든 render() 실행 시에 호출됩니다.

static getDerivedStateFromProps (props, state) {
    // 다음에 올 props를 바탕으로 현재의 state를 변경할 수 있다.
    if(props.name !== state.name) {
    //state가 이렇게 변경된다
    return {
        name:props.name
    }
  }
   //state에 영향을 미치지 않는다
   return null
}

 

 

 

 

getSnapShotBeforeUpdate() : DOM이 업데이트되기 직전에 호출

componentWillUpdate()를 대체할 수 있는 메서드이다.

여기서 반환되는 값은 componentDidUpdate로 전달됩니다.

DOM에 렌더링되기 전 윈도우 크기를 조절하거나, 스크롤 위치를 조정하는 등의 작업을 하는데에 사용됩니다.

 

생명주기 메서드 정리

 

 

 


 

 

 

에러상황에 실행되는 메서드

getDerivedStateFromError() : 에러 상황에서 실행되는 메서드

자식 컴포넌트에서 에러가 발생했을 때 호출되는 에러 메서드 입니다.

static 메서드로, error를 인수로 받습니다.

 

또한 하위 컴포넌트에서 에러가 발생했을 때 어떻게 자식 컴포넌트를 렌더링 할지 결정하는 용도로 사용하는 메서드이기 때문에,

반드시 state값을 반환해야 합니다.

 

 

componentDidCatch() : 에러 상황에서 실행되는 메서드

자식 컴포넌트에서 에러가 발생했을 때 호출되는 에러 메서드 이며,

getDerivedStaefromError()에서 에러를 잡고 state를 결정한 이후에 시행됩니다.

 

두개의 인수를 받으며, 첫 번째는 getDerivedStaefromError()와 동일한 error,

두번째는 어떤 컴포넌트가 에러를 발생시켰는지 정보를 가지고 있는 info 입니다.

 

 

 

이 두 메서드를 조합하여 ErrorBoundary component를 만들어서 사용할 수 있습니다.

ErrorBoundary는 모든 에러를 잡아낼 수는 없고, 경계 내부에 있는 에러만 잡을 수 있습니다.

외부의 에러는 다른 ErrorBoundary를 찾아가거나 찾지 못한다면 일반 JS 코드처럼 에러는 throw됩니다.

class ErrorBoundary extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
         hasError: false 
      };
    }
  
    // 하위 컴포넌트에서 발생한 Error를 인수로 받음
    static getDerivedStateFromError(error) {    
    // 다음 렌더링에서 폴백 UI가 보이도록 상태를 업데이트
    return { hasError: true };  }


    componentDidCatch(error, errorInfo) {    
    // 에러 리포팅 서비스에 에러를 기록할 수도 있습니다.    
       logErrorToMyService(error, errorInfo);  
    }

    render() {
      if (this.state.hasError) {      
        // 에러 발생시 렌더링할 JSX
        return <h1>Something went wrong.</h1>;    
      }

      // 일반적인 상황의 JSX
      return this.props.children;
    }
  }

  // App.tsx
  function App () {
    return (
        <ErrorBoundary>
            <Child />
        </ErrorBoundary>
    )
  }

 

getDerivedStateFromError는 렌더링 과정에서 호출되는 메서드이기 때문에 부수 효과를 발생시켜서는 안됩니다.

부수 효과란 에러에 따른 상태 state를 반환하는 것 이외의 모든 작업을 의미합니다.

반면에 componentDidCatch는 commit 단계에서 실행되므로 부수 효과를 수행할 수 있습니다.

 

 

 

class component에서 제공하는 method로 완성도있는 React app을 만드는데에 충분해 보이는데,
왜 리액트는 function component에 hooks을 도입한 새로운 패러다임을 만든것일까?

class component의 한계
 1. 데이터의 흐름을 추적하기 어렵다.
     생명주기 메서드에서 보이다시피 state의 흐름을 추적하기가 매우 어렵다.
 2. 함수에 비해 상대적으로 어렵다.
 3. 코드를 최적하하기 어렵다.
 4. 핫 리로딩을 하는데 상대적으로 불리하다.
      코드에 변경 사항이 발생했을 때 앱을 다시 시작하지 않고서 변경된 코드만 업데이트해 변경사항을 빠르게 적용하는 기법
      ( 함수형은 state를 함수가 아니 클로저에 저장해 둬서 다시 함수가 실행되도 해당 state를 잃지 않아서 가능)

 

 

 

반응형