리액트 - 훅

아침7시개발·2022년 1월 20일
0

리액트

목록 보기
11/16

Hook은 React 버전 16.8부터 React 요소로 새로 추가되었다.
Hook을 이용하여 기존 Class 바탕의 코드를 작성할 필요 없이 상태 값과 여러 React의 기능을 사용할 수 있다.

import React, { useState } from 'react';

function Example() {
  // "count"라는 새로운 상태 값을 정의
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useState는 “Hook”에서 처음 배우게 될 함수다.

왜 Hook을 React에 추가했는지, 그리고 Hook이 애플리케이션을 작성하는 데 어떠한 도움을 주는지 알아보자.

  1. 선택적 사용
    기존의 코드를 다시 작성할 필요 없이 일부의 컴포넌트들 안에서 Hook을 사용할 수 있다.
  2. 100% 이전 버전과의 호환성
    Hook은 호환성을 깨뜨리는 변화가 없다.
  3. 현재 사용 가능
    Hook은 배포 v16.8.0에서 사용할 수 있다.

React에서 Class를 제거할 계획은 없다고 한다.

Hook은 props, state, context, refs, 그리고 lifecycle와 같은 React 개념에 좀 더 직관적인 API를 제공한다.

Hook 사용 장점

  1. 기존의 class에서는 컴포넌트 사이에서 상태 로직을 재사용하기 어렵다.

    Hook을 사용하면 컴포넌트로부터 상태 관련 로직을 추상화할 수 있다. 이를 이용해 독립적인 테스트와 재사용이 가능하다. Hook은 계층의 변화 없이 상태 관련 로직을 재사용할 수 있도록 돕는다. 이것은 많은 컴포넌트 혹은 커뮤니티 사이에서 Hook을 공유하기 쉽게 만든다.

  2. 복잡한 컴포넌트들은 이해하기 어렵다.

    생명주기 메서드를 기반으로 쪼개는 것 보다는, Hook을 통해 서로 비슷한 것을 하는 작은 함수의 묶음으로 컴포넌트를 나누는 방법을 사용할 수 있다. (구독 설정 및 데이터를 불러오는 것과 같은 로직) 또한 이러한 로직의 추적을 쉽게 할 수 있도록 리듀서를 활용해 컴포넌트의 지역 상태 값을 관리하도록 할 수 있다.


Hook 이란

Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 연동(hook into)할 수 있게 해주는 함수다. Hook은 class 안에서는 동작하지 않는다. 대신 class 없이 React를 사용할 수 있게 한다. React는 useState 같은 내장 Hook을 몇 가지 제공한다. 컴포넌트 간에 상태 관련 로직을 재사용하기 위해 Hook을 직접 만드는 것도 가능하다.


State Hook

버튼을 클릭하면 값이 증가하는 간단한 카운터 예시를 보자.

 import React, { useState } from 'react';

function Example() {
  // "count"라는 새 상태 변수를 선언
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

여기서 useState가 바로 Hook 이다.Hook을 호출해 함수 컴포넌트(function component) 안에 state를 추가했다. 이 state는 컴포넌트가 다시 렌더링 되어도 그대로 유지된다.useState는 현재의 state 값과 이 값을 업데이트하는 함수를 쌍으로 제공한다. 이 함수를 이벤트 핸들러나 다른 곳에서 호출할 수 있다.class의 this.setState와 거의 유사하지만, 이전 state와 새로운 state를 합치지 않는다는 차이점이 있다.(예시 Using the State Hook) useState는 인자로 초기 state 값을 하나 받는다.this.state와는 달리 useState Hook의 state는 객체일 필요가 없다. 이 초기값은 첫 번째 렌더링에서 딱 한번만 사용된다.

여러 state 변수 선언하기

하나의 컴포넌트 내에서 State Hook을 여러 개 사용할 수 있다.

function ExampleWithManyStates() {
  // 상태 변수를 여러 개 선언
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

배열 구조 분해 문법은 useState로 호출된 state 변수들을 다른 변수명으로 할당할 수 있게 한다. React는 매번 렌더링할 때 useState가 사용된 순서대로 실행한다.

State Hook에 대해서는 독립된 문서 Using the State Hook에서 더 알아보자.


Effect Hook

React 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작할 수 있다.이런 모든 동작을 side effects(effects)라고 한다. 이것은 다른 컴포넌트에 영향을 줄 수도 있고, 렌더링 과정에서는 구현할 수 없는 작업이다.

useEffect는 함수 컴포넌트 내에서 이런 side effects를 수행할 수 있게 한다. React class의 componentDidMount 나 componentDidUpdate, componentWillUnmount와 같은 목적으로 제공되지만, 하나의 API로 통합된 것이다.(참조)

아래는 React가 DOM을 업데이트한 뒤에 문서의 타이틀을 바꾸는 컴포넌트다.

import React, { useState, useEffect } from 'react';

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

  // componentDidMount, componentDidUpdate와 비슷
  useEffect(() => {
    // 브라우저 API를 이용해 문서의 타이틀을 업데이트
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect를 사용하면, React는 DOM을 바꾼 뒤에 effect함수를 실행할 것이다.Effects는 컴포넌트 안에 선언되어있기 때문에 props와 state에 접근할 수 있다. 기본적으로 React는 매 렌더링 이후에 effects를 실행한다.(Class 생명주기(lifecycle)와 다른 점은 Using the Effect Hook 문서에서 더 자세히 다루고 있다.)

Effect를 해제할 필요가 있다면, 해제하는 함수를 반환해주면 된다.

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

이 컴포넌트는 친구의 접속 상태를 구독하는 effect를 사용했고, 구독을 해지함으로써 해제한다. 예시에서 컴포넌트가 unmount될 때 React는 ChatAPI에서 구독을 해지할 것이다.또한 재 렌더링이 일어나 effect를 재실행하기 전에도 마찬가지로 구독을 해지한다.props.friend.id가 바뀌지 않았을 때 재구독을 건너뛰도록 설정할 수 있다.

useState와 마찬가지로 컴포넌트 내에서 여러 개의 effect를 사용할 수 있다.

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...

Hook을 사용하면 구독을 추가하고 제거하는 로직과 같이 서로 관련 있는 코드들을 한군데에 모아서 작성할 수 있다. 반면 class 컴포넌트에서는 생명주기 메서드(lifecycle methods) 각각에 쪼개서 넣어야만 했다.

useEffect에 대해서는 독립된 문서 Using the Effect Hook을 보자.


훅의 사용규칙

Hook은 그냥 JavaScript 함수이지만, 두 가지 규칙을 준수해야 한다.

  • 최상위(at the top level)에서만 Hook을 호출해야 한다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 말자.
  • React 함수 컴포넌트 내에서만 Hook을 호출해야 한다. 일반 JavaScript 함수에서는 Hook을 호출해서는 안 된다. (Hook을 호출할 수 있는 곳이 딱 한 군데 더 있는데 직접 작성한 custom Hook 내이다.)
    이 규칙들에 대해서는 독립된 문서 Rules of Hooks에서 더 알아보자.
profile
쉬엄쉬엄하는 개발자

0개의 댓글