본문 바로가기
개발일지/React

React 함수형 컴포넌트와 Class형 컴포넌트 생명주기

by 한삐 2023. 3. 23.
728x90

React state와 LifeCycle

함수형 컴포넌트와 class형 컴포넌트의 LifeCycle이 어떻게 되는지 간단하게 비교해 보는 글이다.

 

클래스형 컴포넌트와 생명주기 메서드

1. Mount(컴포넌트가 처음 실행될 때)

- state, context, defalutProps 저장
- componentWillMount - 안전하지 않은 접근
- render
- componentDidMount - DOM 접근 가능

 

2. Props Update(프롭스가 업데이트될때)

- componentWillReceiveProps - 사용 종료
- shouldComponentUpdate
- componentWillUpdate - 사용 종료
- render
- componentDidUpdate - DOM 접근 가능

 

3. State Update (스테이트가 업데이트됐을 때)

- shouldComponentUpdate
- componentWillUpdate - 사용 종료
- render
- componentDidUpdate - DOM 접근 가능

 

사실상 componentWillReceiveProps와  componentWillUpdate의 사용 종료로 state와 props가 업데이트될 때 동일하게 작동한다.

 

4. Unmount (컴포넌트가 제거되는 것)

- componentWillUnmount

 

사용 종료 기준은 아래와 같다.

 

함수형 컴포넌트와 useEffect 훅

컴포넌트의 실행

- 함수형 컴포넌트 호출

- 함수형 컴포넌트의 내부에서 실행

- return()으로 화면에 렌더링

- 생명주기 메서드 대신 useEffect를 통한 비슷한 처리 가능

 

dependency에 따른 useEffect의 실행

- deps 값이 없는 경우 : 화면이 렌더링 된 이후 수행이 되며, 리렌더링이 발생할 때마다 다시 실행
- deps 값이 빈 배열인 경우 : 첫 렌더링 완료 후 1회만 실행
- deps 값이 존재하는 경우 : 첫 렌더링 완료 후 1회 실행 && deps 값이 변경되었을 경우마다 실행

 

// dep X
useEffect(() => {
    // effect
    return () => {
      // cleanup
    };
  });
  
// dep []
useEffect(() => {
    return () => {
    };
}, []);

// dep [some dep...]
useEffect(() => {
    return () => {
    };
}, [dep]);

 

[Mounting] useEffect() - 컴포넌트 렌더링 이후 실행
- dep 설정에 따라 실행됨

[Updating] useEffect() - 컴포넌트 내에서 변화가 발생했을 경우 실행
- 부모 컴포넌트의 리렌더링, 부모로부터의 props값 변화, 해당 컴포넌트 내에서 state 변경 등

[Unmounting] useEffect() - 컴포넌트 내에서 DOM을 제거할 때 실행되는 메서드
- 컴포넌트의 DOM이 제거될 때 수행되며 useEffect 내부의 return 값이 사용됨

 

위와 같이 컴포넌트를 여닫는 페이지가 있다고 가정했을 때, console을 통해 대략적인 흐름을 확인해 볼 수 있다.

 

 

Class형 컴포넌트

컴포넌트가 처음 실행될 때

 

state가 변경될 때 (props가 변경될 때와 동일)

컴포넌트가 제거될 때

 

 

함수형 컴포넌트

컴포넌트가 처음 실행될 때

컴포넌트가 업데이트될 때

콘솔에  출력된 것과 같이, useEffect의 return 이후 부분은 componentWillUnmount와 비슷하지만, 컴포넌트가 다시 렌더링 되기 전마다 다시 실행된다.

 

컴포넌트가 제거될 때

컴포넌트가 제거될 때는 componentWillUnmount처럼 useEffect의 return 부분이 실행되는 것을 볼 수 있다.

 

 

사용된 코드

// Lifecycle.jsx

import { useState } from "react";
import { FunctionComponent } from "./FunctionComponent";
import { ClassComponent } from "./ClassComponent";

export const Lifecycle = () => {
  const [fnOpen, setFnOpen] = useState(false);
  const [classOpen, setClassOpen] = useState(false);
  const effectComponentHandler = (num) => {
    if (num) {
      setFnOpen(!fnOpen);
    } else {
      setClassOpen(!classOpen);
    }
  };

  return (
    <div>
      <p>Fn component / Class component</p>
      <button onClick={() => effectComponentHandler(1)}>함수 open/close</button>
      <button onClick={() => effectComponentHandler("")}>
        클래스 open/close
      </button>
      {fnOpen ? <FunctionComponent some1={1} some2={2} some3={3} /> : null}
      {classOpen ? <ClassComponent some1={1} some2={2} some3={3} /> : null}
    </div>
  );
};

 

// FunctionComponent.jsx

import { useState, useEffect } from "react";

export const FunctionComponent = (props) => {
  console.log("컴포넌트 렌더링", props);
  const [count, setCount] = useState(0);
  useEffect(() => {
    // effect
    if (count === 0) {
      console.log(`useEffect 첫 등장`);
    } else {
      console.log(`useEffect 다시 등장 count: ${count}`);
    }
    return () => {
      // cleanup
      console.log(
        "useEffect 퇴장 --- componentWillUnmount와 비슷하지만 리렌더링마다 사용됨"
      );
    };
  }, [count]);

  const plus = () => {
    setCount((prev) => prev + 1);
  };
  const minus = () => {
    setCount((prev) => prev - 1);
  };

  return (
    <div>
      <p>함수형 컴포넌트</p>
      <span>{count}</span>
      <button onClick={plus}>+</button>
      <button onClick={minus}>-</button>
      <p>Props 받기</p>
      <div>props1 = {props.some1}</div>
      <div>props2 = {props.some2}</div>
      <div>props3 = {props.some3}</div>
    </div>
  );
};

 

// ClassComponent.jsx

import { Component } from "react";

export class ClassComponent extends Component {
  constructor(props) {
    super(props);
    // Class형 컴포넌트의 state는 무조건 객체 형태여야 한다.
    this.state = {
      counter: 0,
    };
    console.log(
      "Mount - 컴포넌트가 처음 실행될 때 state, context, defalutProps 저장",
      props
    );
  }
  plus = () => {
    this.setState((state) => ({ counter: state.counter + 1 }));
  };
  minus = () => {
    this.setState((state) => ({ counter: state.counter - 1 }));
  };

  componentWillMount() {
    console.log("componentWillMount");
  }
  componentDidMount() {
    console.log("componentDidMount - DOM 접근 가능");
  }
  componentDidUpdate() {
    console.log("componentDidUpdate");
  }
  shouldComponentUpdate() {
    console.log("shouldComponentUpdate - state or props 업데이트");
    return true;
  }
  componentWillUnmount() {
    console.log("componentWillUnmount");
  }

  render() {
    return (
      <div>
        <p>Class형 컴포넌트</p>
        <span>{this.state.counter}</span>
        <button onClick={this.plus}>+</button>
        <button onClick={this.minus}>-</button>
        <p>Props 받기</p>
        <div>props1 = {this.props.some1}</div>
        <div>props2 = {this.props.some2}</div>
        <div>props3 = {this.props.some3}</div>
      </div>
    );
  }
}

 


참조

 

https://ko.reactjs.org/docs/state-and-lifecycle.html

 

State and Lifecycle – React

A JavaScript library for building user interfaces

ko.reactjs.org

https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html

 

Update on Async Rendering – React Blog

This blog site has been archived. Go to react.dev/blog to see the recent posts. For over a year, the React team has been working to implement asynchronous rendering. Last month during his talk at JSConf Iceland, Dan unveiled some of the exciting new possib

legacy.reactjs.org

 

728x90

댓글