SUIN

[React] React Hook 본문

React

[React] React Hook

choi suin 2021. 11. 28. 15:56
728x90

 

앞의 포스팅에서 클래스과 함수  컴포넌트의 생명주기에 대하여 알아보았다 

https://suinchoi.tistory.com/40

 

[React] 컴포넌트 생명주기 / Class LifeCycle

컴포넌트 생명주기( React LifeCycle) 이란? 개발을 할때 어떤 타이밍에 어떤 순차적으로 동작들이 일어나는가 , 즉 화면에 컴포넌트가 나타났다가 사라지기 까지의 모든 과정 을 라이프 사이클이

suinchoi.tistory.com


 React Hook 

  • Hook은 React 버전 16.8 버전부터 React 요소로 새로 추가되었다 
  • Hook을 이용하여 기존 Class 바탕의 코드를 작성할 필요 없이 상태 값과 여러 React의 기능을 사용할 수 있다 
  • 클래스 컴포넌트에서는 훅이 동작하지 않는다.

 

리액트 훅이 왜 나오게 되었는가? 

1.컴포넌트 사이의 상태 로직을 재사용하기 어려웠다.

- 클래스 컴포넌트의 this.state로 상태를 관리하다 보니 재사용하는 불편함이 있었다  React Hook에서는 계층의 변화 없이 로직을 그대로 사용할 수 있다 

2. componentDidMount 이벤트 리스너를 설정하고 componentWillUnmount 에서 삭제를 하는데 해야 하는데 

비슷한 동작의 상호관련코드는 분리되지만 관련 없는 코드는 단일 메서드를 결합해야 하므로 버그가 너무 쉽게 발생했다.

React Hook에서는 생명주기 기반으로 쪼개기 보다는 hook을 통해서 비슷한 기능들을 작은 함수 단위로 묶어서 쓸 수 있다 

 

>> React Hook은 class의  단점을 보완하고 라이프 사이클등과 관련된 함수를 재사용 가능하도록 하기 위해 훅이 나오게 되었다고 한다 

그래서 React 에서는 클래스 컴포넌트가 아닌 함수 컴포넌트의 hook 사용을 권장한다


Hook 규칙 

1. 최상위(at the Top Level)에서만 Hook을 호출해야 합니다.

  • 반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 마세요
  • early return이 실행되기 전에 항상 React 함수의 최상위(at the top level)에서 Hook을 호출해야 합니다. 이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장

 2. React 함수 내에서 Hook을 호출해야 합니다

  •  React 함수 컴포넌트에서 Hook을 호출하세요
  • Custom Hook에서 Hook을 호출하세요.

 State Hook 

 

    선언    

클래스 컴포넌트의 state 선언 👇🏻

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

 

함수 컴포넌트의 state 선언  useState() 👇🏻      - 배열 구조 분해 문법 사용하여 선언 

import React, { useState } from 'react';

function Example() {
  // 새로운 state 변수를 선언하고, 이것을 count라 부르겠습니다.
  const [state, setState] = useState(0);
  
  //state 변수와  해당 변수를 갱신할 수 있는 함수 이 두 가지 쌍을 반환한다.
   const [변수, 갱신할 함수] = useState(초기값);
  • useState() - Hook의 인자로 넘겨주는 값은 state의 초기 값
  • state- 변수
  • setState  - 해당 변수를 갱신할 수 있는 함수 

 

    갱신    

클래스 컴포넌트의 state 갱신  👇🏻

<button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>

 

함수 컴포넌트의 state 갱신  👇🏻        - setState state 변수를 가지고 있으므로 this를 호출하지 않아도 됩니다.

<button onClick={() => setState(state + 1)}>
    Click me
  </button>

 Effect Hook 

 useEffect은  Hook을 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것

- class메서드가 관련 없는 로직들은 모아 두고 관련 있는 로직들은 여러 개의 메서드로 나누어 놓는 경우가 있는데 이를 해결하기 위해 hook의 useEffect에서 에서 하나로 합치고 버그를 방지할 수 있다 

//기본
useEffect(() => {});

//useEffect는  기본적으로 첫번째 렌더링과 이후의 모든 업데이트에서 수행된다.

 

 side effect 란?

데이터 가져오기, 구독(subscription) 설정하기, 수동으로 React 컴포넌트의 DOM을 수정하는 것까지 이 모든 것

- Effect Hook을 사용하면 함수 컴포넌트에서 side effect를 수행할 수 있다.

 

 

정리(Clean-up)를 이용하지 않는 Effects

React가 DOM을 업데이트한 뒤 추가로 코드를 실행해야 하는 경우가 있다

네트워크 리퀘스트, DOM 수동 조작, 로깅 등은 정리(clean-up)가 필요 없는 경우들입니다. 이러한 예들은 실행 이후 신경 쓸 것이 없다 

예시) 클래스 컴포넌트에서는 상태가 바뀔 때마다 타이들 내용을 바꾸고 싶은데 이경우 componentDidMount와  componentDidUpdate 두 곳에 동일한 코드를 작성해 주어야 한다.(중복 코드 작성이 됨)

하지만 hook의 Effects에서는  한 줄로 componentDidMount와 componentDidUpdate 적용할 수 있다. 

//클래스 컴포넌트 

constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  
 componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }
//함수 컴포넌트 

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

 

정리(clean-up)를 이용하는 Effects

예시) 구독을 예로 들어보자

클래스 컴포넌트에서는 무언가를 구독을 해야 동작하고 componentWillUnmount시 구독을 해지해주어야 한다.

componentDidMount componentWillUnmount가 서로 대칭을 이루고 있는다

 두 개의 메서드 내에 개념상 똑같은 effect에 대한 코드가 있음에도 불구하고 생명주기 메서드는 이를 분리시킨다.

 

Unmount 해주지 않을 시 / 구독을 해지하지 않을 시   메모리 상의 구독의 로직들 남아있어서 memory leak 현상이나  여러 가지 이슈가 발생할 수 있다 

클래스 컴포넌트  👇🏻 

//구독중
componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  //구독해제
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }

 

함수 컴포넌트  👇🏻   

hook에서는 useEffect에서 다 같이 관리를 할 수 있게 했다. ( React는 컴포넌트가 마운트 해제되는 때에 정리(clean-up)를 실행)

useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // effect 이후에 어떻게 정리(clean-up)할 것인지 표시합니다.
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

 

Effect를 건너뛰어 성능 최적화하기 (dependency array)

모든 props와 state가 변경이 되었을 때 Effect가 동작하게 되는데 디펜던시 어레이를 사용해서 해당 state가 변경되었을 때만 Effect 동작할 수 있게 할 수 있다. 

- 특정 값들이 리 렌더링 시에 변경되지 않는다면 React로 하여금 effect를 건너뛰도록 할 수 있다

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // count가 바뀔 때만 effect를 재실행합니다.

[] 값만 선언 시 Mount() 될 때만 한번 동작할 수 있도록 할 수도 있다. 

 

 

 

 


공식문서

https://ko.reactjs.org/docs/react-component.html

https://ko.reactjs.org/docs/hooks-intro.html