REACT Hooks

Hyein Son·2020년 7월 12일
0

Hooks

함수형 컴포넌트에서도 state관리, lifecycle과 같은 class형 컴포넌트에서만 가능하던 기능들을 사용할 수 있도록한 것이다.


Class형 컴포넌트의 단점

1. Logic의 재사용이 어렵다.

부분적인 API 사용 및 state를 관리하는 로직을 재사용하는데 제약이 있다.

2. 코드가 길고 복잡해진다.

constructor, this, binding 등 기본 규칙을 따라야하기 때문에 코드가 복잡해진다. 또, lifecycle때문에 같은 로직을 다른 method내에서 사용하는 경우가 있다.(componentDidMount, componentWillUnmount)


Hooks의 장점

1. 코드가 간결해지고 가독성이 좋아진다.

같은 로직을 한 곳으로 모을 수 있기 때문에 응집성이 좋아지고 가독성이 좋아진다.

2. 재사용 가능한 로직을 쉽게 만들 수 있다.

함수안에서 다른 함수를 호출해 새로운 훅을 만들 수 있다. 클래스형 컴포넌트의 lifecycle method를 useState와 useEffect를 조합해 표현할 수 있다.

3. 정적 타입 언어로 타입을 정의하기 쉽다.


기본 Hook

useState

import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0); // count의 초기값 '0'
  return (
     <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
     </div>
    );
}

useState를 이용해 count라는 state변수와 state값을 변화시켜주는 함수 setCount를 만든다. useState의 인자는 state변수의 초기값이 된다.
버튼을 클릭하면 setCount를 호출해서 state값을 변화시킨다.

useEffect

-클래스형 컴포넌트의 componentDidMountcomponentDidUpdate, componentWillUnmount가 합쳐진 것이다.
-렌더 이후 어떤 일을 수행해야하는지를 말한다.
-렌더 이후 매번 수행된다.

정리가 필요없는 경우

-componentWillUnmount가 필요없는 경우

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

렌더할때마다 state변수 count를 불러온다. 함수안에서 useEffect를 불러내기 때문에 state변수에 접근할 수 있다.(자바스크립트의 클로저를 이용)

정리가 필요한 경우

-componentWillUnmount가 필요한 경우

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    }; // 정리를 위한 함수
  });

정리를 위한 함수를 반환해 구독을 추가, 제거하는 로직을 묶어둔다. 하나의 effect를 구성한다. 렌더될 때마다 useEffect가 실행되므로 다음 effect가 실행하기 전에 이전의 렌더링에서 파생된 effect를 정리하게 된다. 이것은 클래스형 컴포넌트의 componentDidUpdatecomponentWillUnmount에서의 중복을 피하고 관련 코드를 한곳에 모아두기 때문에 버그를 방지하는데 도움이 된다.

❗️리액트의 흔한 버그

componentDidUpdate를 제대로 다루지 않는다는 것이다.
위의 코드를 클래스형으로 작성할 경우, componentDidUpdate가 필요하다.

  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate(prevProps) {
    // 이전 friend.id에서 구독을 해지합니다.
    ChatAPI.unsubscribeFromFriendStatus(
      prevProps.friend.id,
      this.handleStatusChange
    );
    // 다음 friend.id를 구독합니다.
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

컴포넌트가 화면에 표시되어 있는 동안 friend.id가 변화하면 마운트해제가 일어날때는 다른 친구의 id를 사용하게 된다. 이를 해결하기 위해서 componentDidUpdate를 사용하는 것이다. friend.id가 변화하게 되면 componentDidUpdate가 실행되어 이전의 friend.id는 구독해지 하고 변화한 friend.id를 구독한다.

훅에서는 effect가 렌더할 때마다 실행되기 때문에 렌더할때마다 이전의 effect를 정리해 componentDidUpdate를 사용하지 않아 생기는 문제를 쉽게 해결할 수 있다.

하지만 모든 렌더링마다 effect를 실행시키는 것은 성능저하 문제를 야기할 수 있다. 이를 해결하기 위해

useEffect(() => {
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
}, [props.friend.id]); // props.friend.id가 바뀔 때만 재구독합니다.

useEffect의 두번째 인자로 배열을 넘기면 이것이 변화할때마다 실행해 최적화가 가능하다.
이것은 componentDidMount, componentWillUnmount,componentDidUpdateuseEffect 하나로 대체할 수 있는 것을 의미한다.

effect를 한번만 실행 하기

useEffect의 두번째 인자로 [] 빈배열을 넘기면 effect가 props, state를 모두 의존하지 않게되므로 재실행되지 않고 렌더 후 딱 한번만 실행된다.
이것은 componentDidMountcomponentWillUnmount에 가깝다.

0개의 댓글