[React] Custom Hook

Seju·2023년 8월 21일
3

React

목록 보기
3/9

useFecth

🎉 Custom Hook이란?


React 커스텀 훅에 관한 공식 문서

  • 커스텀 훅(Custom Hook)이란 React에서 가독성과 코드 재사용성을 향상 시키기 위해 도입된 개념
  • 커스텀 훅은 기본적으로 사용자가 정의한 함수로, 여러 컴포넌트에서 공통적으로 사용되는 로직을 분리해서 재사용할 수 있도록 도와준다.
  • 이러한 방식으로 공통적인 기능이나 상태 관련 로직을 묶어서 한 곳에서 관리 할 수 있게된다.
  • 커스텀훅은 일반 함수처럼 Javascript로 작성되며, 훅(Hook)이라는 이름에서 알 수 있듯이, 내부에서 React의 기본 훅을 사용을 허용된다.
    • useStateuseEffect같은 기본훅을 커스텀 훅 내부에서 사용가능

🎊 커스텀 훅 작성 규칙


  1. react에서의 커스텀 훅은 use라는 접두사를 붙여서 사용해야한다.
  2. 커스텀 훅 내에서만 react에서 제공하는 hook 허용, 나머지는 ❌
  3. 커스텀 훅은 컴포넌트 코드 처럼 순수해야한다
  4. 훅에서 다른 훅으로 반응성 값을 전달할 수 있으며, 최신상태를 유지한다.
  5. useMount처럼 애매한 목적의 커스텀훅을 만들어선 ❌
    목적을 구체적으로 정한뒤 만들어야한다.

✨ 커스텀 훅을 사용하면 상태 자체가 아닌, 상태 저장 로직을 공유한다.

커스텀훅 useOnlineState

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    globalThis.addEventListener('online', handleOnline);
    globalThis.addEventListener('offline', handleOffline);
    return () => {
      globalThis.removeEventListener('online', handleOnline);
      globalThis.removeEventListener('offline', handleOffline);
    };
  }, []);
  return isOnline;
}

위 커스텀훅을 아래 코드에서 적용 시켰을때, 위 둘의 컴포넌트가 같은 상태를 공유할까

function StatusBar() {
  const isOnline = useOnlineStatus();
  // ...
}

function SaveButton() {
  const isOnline = useOnlineStatus();
  // ...
}

커스텀 훅으로 추출하기 이전으로 생각해보면?

  • 아래 두개의 컴포넌트는 서로 다른 완전히 독립적인 상태와 Effect를 가지게된다.
function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // ...
  }, []);
  // ...
}

function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // ...
  }, []);
  // ...
}

🧙 fetch를 담당하는 useFetchData Custom Hook

기본적으로 fetch와 관련된 커스텀훅이기때문에, useEffect()를 사용한다.

  • 매개변수로 endpoint, 즉 fetch할 url을 동적으로 받으며, 세가지 상태를 가진다.

    • 실제 통신에 성공했을때 data를 담는 [data, setData] = useState([]);
    • 로딩중 -> 로딩 종료된 상태를 나타내는 [isLoading, setIsLoading] = useState(false);
    • 에러가 발생했을때 error 객체를 담는 역할을 하는 [error, setError] = useState(null);
  • try/catch문으로 감싸며, 통신에 성공했을땐 setDataresponseData를 받아 data 상태를 update하며, error가 발생했을땐, setErrorerror객체를 담아 error 상태를 update한다.

📝 useFecthData.js

import { useEffect, useState } from 'react';

export default function useFetchData(endpoint) {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();

    setIsLoading(true);

    async function fetchProducts() {
      try {
        const response = await fetch(endpoint, {
          signal: controller.signal,
        });
        const responseData = await response.json();
        setData(responseData);
      } catch (error) {
        if (!(error instanceof DOMException)) {
          setError(error);
        }
      } finally {
        setIsLoading(false);
      }
    }

    fetchProducts();

    // StrictMode(x2, detect impure function component)
    // mount(1, 요청 1) → unmount (취소 1) → mount(2, 요청 1) -> 응답 1
    return () => {
      controller.abort();
    };
  }, [endpoint]);

  return { data, isLoading, error };
}

또한 위 코드에서 AbortController 객체가사용되는데,

MDN AbortController

  • AbortControllerabort 메서드로 useEffect에서 cleanupFunction내에서 fetch를 취소하는 역할을 담당한다

취소하는 이유는

  • React에선 컴포넌트의 순수성을 판단하기위해 <StrictMode>를 사용한다.

  • 그러나 useEffect는 최초 렌더링 시 1회가 무조건적으로 실행되는데 <StrictMode>로 인하여, fetch로 서버로부터 가져온 data가 불필요하게 2번 호출 및 렌더링되어 웹의 성능 저하에 영향을 줄 수 있게 된다.

  • 따라서, useEffect에서 1번째 렌더링 후 컴포넌트가 unMount될때, useEffectcleanupFunction에 의해 , 첫번째 fetch 요청을 취소하고 2번째 fetch만 진행 할 수 있는 역할을 한다.

profile
Talk is cheap. Show me the code.

1개의 댓글

comment-user-thumbnail
2023년 8월 21일

Abortcontroller를 활용한 커스텀 fetch 훅 감명 받았습니다. 좋아요 누르고 갈게요

답글 달기