React Hook이란 무엇인가?

minjeong·2023년 1월 18일
0

클래스 컴포넌트가 아닌 함수형 컴포넌트로 개발해야 한다는 것을 알게 되었다. (아무것도 모른 채 학습을 시작했던 리액트 튜토리얼의 틱택토 게임은 클래스 컴포넌트 코드를 제공한다.) 그래서 튜토리얼의 모든 컴포넌트를 함수로 리팩토링 하는데, 바로 props/state 데이터를 어떻게 처리해야 하는지에 대한 선행 학습 문제가 바로 따라온다.

그리고 친절히 클래스가 아니어도 상태 값을 관리하며 여러 React 기능을 사용할 수 있도록 지원하는 Hook API 안내가 공식 문서에 작성되어 있었다.

출처 Reactjs.org, Hook의 개요


Hook

함수 컴포넌트에서 React의 주요 개념(상태관리, 생명주기 기능 등)을 "연동(hook into)" 할 수 있게 해준다.

  • React 16.8.0 / React Native 0.59버전에서부터 Hook을 지원하기 시작했다.
  • props, state, context, refes, lifecycle과 같은 React 개념에 보다 직관적인 API를 제공한다.
  • 클래스 컴포넌트 안에서는 동작하지 않는다. 클래스로 만들지 않고 React를 사용하게 해주는 것이다.

Motivation

"왜 Hook이 등장했을까?"

  • 기존에는 render props, 고차 컴포넌트 패턴으로 컴포넌트 간 재사용 로직을 사용하게 했다. 그러나 이 방법들은 컴포넌트의 재구성을 강요하고 코드의 래핑이 중첩되는 문제를 야기시켰다.
  • 함수형 컴포넌트에 Hook API를 제공함으로써 계층의 변화 없이 상태 관리 등의 React 개념과 관련된 로직을 재사용할 수 있도록 도와준다. render props: 컴포넌트 간 코드를 공유하기 위한 테크닉.
    고차 컴포넌트: 컴포넌트를 parameter로 받아서 새 컴포넌트를 반환하는 함수.

Rules

JavaScript 함수이지만 두 가지의 규칙을 준수해야 한다.

  1. 최상위 레벨에서만 Hook을 호출할 것. 반복문, 조건문, 중첩 함수 내에서 Hook을 실행시키지 않는다.
  2. React 함수 컴포넌트 내에서만 Hook을 호출한다. 컴포넌트가 아닌 일반 JavaScript 함수에서는 Hook을 호출하지 않는다. (직접 작성한 Custom Hook에서도 가능.)

해당 규칙을 강제하기 위해 eslint-plugin-react-hooks를 제공하고 있다.

더 읽어보기: https://ko.reactjs.org/docs/hooks-rules.html

State Hook

function Example() {
  const [count, setCount] = useState(0);
}
  • 함수 컴포넌트는 this를 가질 수 없기 때문에 this.state를 할당하거나 읽을 수 없어, useState Hook을 컴포넌트에 호출하여 사용한다. 기능적으로 같다.
  • useState의 parameter는 해당 state의 초기값이다.
  • useState는 state 변수, 해당 변수를 갱신할 수 있는 함수로 이뤄진 쌍을 반환한다. 따라서 구조 분해 할당(destructuring assignment)를 이용하면 한 문장으로 된 코드로 선언, 초기화할 수 있는 것이다.

Effect Hook

React의 "Effect"(=side effect)는 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작하는 작업 등의 모든 동작을 뜻한다.

  • useEffect는 함수 컴포넌트 내에서 side effect, 즉 렌더링 이후 어떤 일을 수행할 수 있게 해준다. (ex. 데이터 가져오기, API 불러내기 등)
  • 클래스 컴포넌트의 componentDidMount, componentDidUpdate, componentWillUnmount와 같은 목적이나 하나의 API로 통합되었다.
  • useEffect는 컴포넌트 안에 선언되어 있으므로 props/state에 접근 가능하다.

useEffect를 컴포넌트 안에서 불러내는 이유

  • 컴포넌트 내부에 둠으로써 props/state의 값에 접근할 수 있다. 함수 범위 안에 존재하므로 특별한 API 없이 가능한 것이다.
  • JavaScript의 Closure를 이용하여 문제를 해결하는 방법이다.

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

  • 정리가 필요한 effect의 가장 대표적인 예시; 외부 데이터를 구독(subscription)해야하는 경우 subscribe 이후 컴포넌트가 제거될 때 unsubscribe 처리가 필요하다.
  • 클래스 컴포넌트에서는 componentDidMount에서 구독을 설정하고 componentWillUnmount에서 구독을 해제하는 방식으로 구현할 것이다.
  • 하지만 Hook을 이용할 경우, effect가 함수를 반환하면 React는 그 함수를 정리가 필요할 때, 즉 컴포넌트의 생명주기가 끝날 때 실행시킨다.
  • 공식 문서의 예제에서는 cleanup()이라는 이름의 함수를 반환했으나, 다른 이름으로 작성하거나 화살표 함수로 반환해도 무관하다고 한다.
function Example(props) {
  const [isOnline, setIsOnline] = useState(null);
  
  function handleChatStatusChange(status) {
  	setIsOnline(status.isOnline);
  }
  
  useEffect(() => {
    ChatAPI.subscribeChatStatus(props.id, handleChatStatusChange);
    
    // clean-up
    return function cleanup() {
      ChatAPI.unsubscribeChatStatus(props.id, handleChatStatusChange);
    }
  });
  
  if (isOnline === null) {
    return 'Loading...';
  }
  
  return isOnline ? 'Online' : 'Offline';
}

React가 effect를 정리(clean-up)하는 시점이란?

React는 컴포넌트가 마운트 해제될 때 정리를 실행한다.

useEffect는 렌더링 이후 매번 수행된다.

  • 기본적으로 첫번째 렌더링과 이후의 모든 업데이트에서 수행된다.
    (첫번째 렌더링이란 JSX 코드의 렌더링을 말하는 게 아닐까.)
  • effects scheduled with useEffect don't block the browser from updating the screen.: 한국어로 된 공식 문서의 문장이 괜히 더 헷갈리는 것 같아 원문 첨부. 아마 effect 내 코드를 수행하는 동안 일어나는 브라우저의 화면 렌더링 작업이 비동기로 계속 이루어질 수 있다는 뜻 같다.

리렌더링 조건으로 성능 최적화

  • 특정 값들이 변경된 렌더링 이후에만 effect가 실행되도록 조건을 걸 수 있다.
  • useEffect의 두 번째(선택적) parameter로 배열을 작성한다.

(더 공부하고 이해해야 하는 사항)

  • 컴포넌트 범위 내에서 바뀌는 값, effect에 의해 사용되는 값을 모두 포함한다.
  • effect를 실행하고 정리(clean-up)하는 과정을 딱 한 번만 실행하고 싶다면 빈 배열([])을 두 번째 인수로 넘겨준다.
  • exhaustive-deps 규칙을 eslint-plugin-react-hooks 패키지에 포함하면 의존성이 바르지 않게 지정되었을 때 경고해준다.

etc.

useContext()

useReducer()


Hooks API Reference 공식 문서로 공부할 것.

profile
신입 개발자 👩‍💻

0개의 댓글