[React] Effect Hook 사용하기

호두파파·2021년 4월 6일
0

React

목록 보기
5/38

본 문서는 리엑트 공식문서와 벨로퍼트 모던 자바스크립트 교제를 참조했습니다.

원문보기

리액트 공식문서
벨로퍼트 모던 자바스크립트 리액트

useEffect 사용하기

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

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

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

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

useEffect가 하는 일은 무엇일까?

useEffect Hook을 이용해 우리는 리액트에게 컴포넌트가 랜더링 이후에 어떤 일을 수행해야 하는지를 말한다. 리액트는 우리가 넘긴 함수를 기억했다가 (이 함수를 effect라고 부른다.) DOM 업데이트를 수행한 이후에 불러낸 것이다. 위의 경우에는 effect를 통해 문서 타이틀을 지정하지만, 이 외에도 데이터를 가져오거나 다른 명령형(imperative) API를 불러내는 일도 할 수 있다.

useEffect를 컴포넌트 안에서 불러내는 이유는 무엇일까?

useEffect를 컴포넌트 내부에 둠으로써 effect를 통해 count state 변수(또는 그 어떤 prop에도)에 접근할 수 있게 된다. 함수 범위 안에 존재하기 때문에 특별한 API 없이도 값을 얻을 수 있는 것이다. HOOK은 자바스크립트의 클로저를 이용해 리엑트에 한정된 API를 고안하는 것보다 자바스크립트가 이미 가지고 있는 방법을 이용해 문제를 해결한다.

useEffect는 렌더링 이후에 매번 수행될까?

기본적으로 첫번째 렌더링과 이후의 모든 업데이트에서 수행된다. 마운팅과 업데이트라는 방식으로 생각하는 대신 effec를 렌더링 이후에 발생하는 것으로 생각하는 것이 더 쉽다. 리액트는 effect가 수행되는 시점에 이미 DOM이 업데이트 되엇음을 보장한다.

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

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

count state 변수를 선언한 뒤 리액트에게 effect를 사용함을 말하고 있다.
useEffect Hook에 함수를 전달하고 있는데 이 함수가 바로 effect이다.
이 effect 내부에서 document.title이라는 브라우저 API를 이용해 문서 타이틀을 지정한다. 같은 함수 내부에 있기 때문에 최신의 Count를 바로 얻을 수 있따. 컴포넌트를 렌더링할 때 리액트는 우리가 이용한 effect를 기억했다가 DOM을 업데이트한 이후에 실행한다. 이는 맨 첫 번째 렌더링은 물론 그 이후의 모든 렌더링에 똑같이 적용된다.

useEffect에 전달된 함수가 모든 렌더링에서 다를 수 있다. 이는 의도된 것으로서, count 값이 제대로 업데이트 되는지에 대한 걱정 없이 effect 내부에서 그 값을 읽을 수 있게 하는 부분이기도 하다. 리 랜더링하는 때마다 모두 이전과 다른 effect로 교체해 전달한다. 이 점이 렌더링의 결과의 한 부분이 되게 만드는 점인데, 각각의 effect는 특정한 렌더링에 속한다.

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

외부 데이터에 구독(subscription)을 설정해야 하는 경우를 생각해보자. 이런 경우에 메모리 누수가 발생하지 않도록 정리(clean-up)하는 것은 매우 중요하다.

정리의 실행을 위해 별개의 effect가 필요하다고 생각할 수 있다. 하지만 구독(subscription)의 추가와 제거를 위한 코드는 결합도가 높기 때문에 useEffect는 이를 함께 다루도록 고안되었다. effect가 함수를 반환하면 리엑트는 그 함수를 정리가 필요할 때에 실행시킬 것이다.

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  
  useEffect(() => {
    function handelStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    chatAPI.subscribeToFriendStatus(props.friend,id, handleStatusChange);
    // effect 이후에 어떻게 정리(clean-up)할 것인지 표현한다. 
    return function cleanUp() {
      chatAPI.unsubscribeFromFriendStatus(props.friend.id, handelStatusChange);
    };
  });
  
  if(isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'OffLine;;
}

effect에서 함수를 반환하는 이유는 무엇일까?

effect를 위한 추가적인 정리 메커니즘이다. 모든 effect는 정리를 위한 함수를 반환할 수 있따. 이 점이 구독(subscription)의 추가와 제거를 위한 로직을 가까이 묶어둘 수 있게 한다. 구독(subscription)의 추가와 제거가 모두 하나의 effect를 구성한다.

리액트가 Effect를 정리(clean-up)하는 시점은?

리액트는 컴포넌트가 마운트 해제되는 때에 정리(clean-up)를 실행한다. 하지만 위의 예시에서 보았듯이 effect는 한 번이 아니라 렌더링이 실행되는 때마다 실행된다. 리액트가 다음 차례의 effect를 실행하기 전에 이전의 렌더링에서 파생된 effect또한 정리하는 이유가 바로 이 때문이다.


요약

useEffect가 컴포넌트의 렌더링 이후에 다양한 side Effect를 표현할 수 있음을 배워보았다. effect에 정리(clean-up)가 필요한 경우에는 함수를 반환한다.

useEffect(() => {
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
});
//정리(clean-up)가 필요없는 경우에는 어떤 것도 반환하지 않는다. 
useEffect(() => {
  document.title = `you clicked ${count} times`;
});

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글