main

리액트 딥다이브 살펴보기 V

Log
7

오늘은 2-3장을 읽고 정리한 내용을 포스팅합니다.

사실 저는 클래스형 컴포넌트 부분을 읽고 되게 많은 내용이 있을거라고 생각했는데, 생명주기에 대한 내용이 주되어서 그에 대해 정리해볼까 합니다.

231228-175325

함수형 컴포넌트와 다르게 클래스형 컴포넌트에는 생명주기가 존재했는데요, 오늘은 생명주기에 대해서 순서대로 정리해보겠습니다.

📌 Mount (마운트될 때)

  • constructor()
    컴포넌트를 새로 만들 때마다 호출되는 생성자 함수
import { Component } from 'react
class ReactComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    }
  }
}

위의 코드와 같이 매번 super를 써주어 Props를 상속해주어야합니다. 또한 초기값인 state를 설정해줄 수 있습니다. 클래스형 컴포넌트 내부에서 state는 늘 객체로 초기화해주어야합니다.

  • static getDerivedStateFromProps()
    props로 받아온 값을 state에 넣어주고 싶을 때 사용하는 메소드입니다. 이전에 deprecatedcomponentWillReceiveProps를 대체하는 메서드입니다. static으로 정의되어있기 때문에 this에 접근할 수 없습니다. 따라서 이 메서드에서 반환하는 객체는 해당 객체의 내용이 모두 state에 들어가게 됩니다.
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
  if(props.name !== state.name) {
    // state가 아래로 변경된다.
    return {
      name: props.name
    }
  }
  
  return null
}
  • render()

  • componentDidMount()
    컴포넌트가 마운트 된 이후의 작업을 처리합니다. API요청같은 부분을 여기서 작업합니다.


📌 Update (업데이트될 때)

  • static getDerivedStateFromProps()
    위에 설명이 있으므로 생략합니다.

  • shouldComponentUpdate()
    state나 props의 변화가 컴포넌트의 출력 결과에 영향을 미치는지 여부를 확인하는 메소드입니다. 쉽게 말해 리렌더링을 할지 말지의 여부를 결정합니다. 리턴값으로 boolean을 내뱉습니다. true를 리턴하면 렌더링을 진행하고, false면 렌더링을 진행하지 않습니다.

shouldComponentUpdate(nextProps, nextState) {
  return nextState.number % 10 !== 4; // 숫자의 마지막 자리가 4면 리렌더링 하지 않는다.
}
  • render()

  • getSnapshotBeforeUpdate()
    컴포넌트의 변화가 DOM에 반영되기 전의 DOM 상태(e.g. 스크롤 위치), 즉 스냅샷을 얻을 수 있는 메소드입니다.
    스냅샷 값을 반환하거나 null을 반환합니다. 이 메소드에서 반환하는 값은 componentDidUpdate() 메소드의 3번째 인자로 전달되게 됩니다. 잘 사용되지는 않지만 채팅 화면처럼 스크롤 위치를 따로 처리하는 작업이 필요한 경우 등에 사용합니다.

getSnapshotBeforeUpdate(prevProps, prevState) {
  if (prevProps.color !== this.props.color) { // 이전 color와 현재 color가 다르다면
    return this.myRef.style.color; // 스냅샷 반환
  }
  return null;
}
  • componentDidUpdate()
    컴포넌트 업데이트(리렌더링)를 마치고, 화면에 우리가 원하는 변경사항이 반영되고 난 뒤 호출되는 메소드입니다.
    여기서도 this.setState를 사용할 수 있지만, 조건적으로 발생하게 하지 않는 이상, 무한 렌더링이 일어날 수 있습니다.
componentDidUpdate(prevProps, prevState, snapshot) {
  if (snapshot) { // 없으면 undefined가 들어온다.
    console.log('업데이트되기 전 색상: ', snapshot);
  }
}

📌 UnMount (언마운트될 때)

컴포넌트가 언마운트 되기 이전에 실행하는 메서드입니다. 이벤트리스너, 타이머와 같은 작업들을 정리합니다.
useEffect의 리턴문과 비슷하게 작동합니다.

componentWillUnmount() {
  clearTimeout(timer);
}

📌 Component와 PureComponent의 차이

클래스형 컴포넌트는 두 가지 유형이 존재합니다. 바로 ComponentPureComponent인데요, 이 둘의 차이는 생명주기를 다루는데에 있다고 합니다.

아래 예시를 보면, 컴포넌트를 확장받은 클래스와, 퓨어컴포넌트를 확장받은 클래스가 있습니다. 둘의 코드는 모두 동일합니다.
버튼을 클릭하면 state.counter 값이 변경되지만, 1로 값이 고정됩니다.

interface State {
  counter: number;
}
 
type Props = Record<string, never>;
 
export class ReactComponent extends React.Component<Props, State> {
  private renderCounter = 0;
 
  constructor(props: Props) {
    super(props);
    this.state = {
      counter: 1,
    }
  }
 
  private handleClick = () => {
    this.setState({counter : 1});
  }
 
  public render() {
    console.log('ReactComponet', ++this.renderCounter);
    return (
      <h1>
        ReactComponent: {this.state.counter}{' '}
        <button onClick={this.handleClick}>+</button>
      </h1>
    )
  }
}
 
export class ReactPureComponent extends React.PureComponent<Props, State> {
  private renderCounter = 0;
 
  constructor(props: Props) {
    super(props);
    this.state = {
      counter: 1,
    }
  }
 
  private handleClick = () => {
    this.setState({counter : 1});
  }
 
  public render() {
    console.log('ReactComponet', ++this.renderCounter);
    return (
      <h1>
        ReactComponent: {this.state.counter}{' '}
        <button onClick={this.handleClick}>+</button>
      </h1>
    )
  }
}
 
export default function CompareComponent() {
  return (
    <>
      <h2>React.Component</h2>
      <ReactComponent />
      <h2>React.PureComponent</h2>
      <ReactPureComponent />
    </>
  )
}

둘의 클래스 모두 state가 변경되었기 때문에 모두 리렌더링 될 것 같지만, Component만 리렌더링이 되고 PureComponent는 리렌더링 되지 않습니다. 왜냐하면 PureComponent얕은 비교를 수행하기 때문입니다.
하지만 얕은 비교만 수행되기 때문에 만약 state값이 이중 객체라면 계속 리렌더링 될 것 입니다. 따라서 state가 단일 객체일 때나, 적재적소에 적용한다면 최적화 될 것 입니다. 함수형 컴포넌트에서 비슷한 기능은 memo와 같습니다.


김다은 이모지
Daeun Kim
Junior Frontend Engineer