리액트 커스텀 훅

문제

  • React Component 는 View 렌더링의 역할이 중요하다

  • Component는 다음과 같은 로직을 중복적으로 담고 있을 수 있다.

    • 데이터 필터링 또는 파싱작업

    • 데이터 요청 / 응답 처리

    • 인증 처리

  • 이런 로직을 분리 한다면 View 그 차제 역할로 순수해지고, 간결해진다.

  • 다만 저런 로직은 re-rendering과정에서 필요한 상태를 가지고 있을 수 있다.

  • React는 커스텀 훅을 통해서 View와 통합해서 렌더링 할 수 있는 방법을 제시한다.

데이터 통신이 필요한 코드

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

const MyComponent = () => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      const url = "https://jsonplaceholder.typicode.com/todos";
      const res = await fetch(url);

      if (res.ok) {
        const list = await res.json();
        setData(list);
      } else {
        console.error("Network response was not ok");
      }

      setLoading(false);
    };

    fetchData();
  }, []);

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      <h1>Data from API</h1>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
};

view component 에서 통신로직 분리

useFetch 라는 커스텀훅을 만들어서 분리시킴

import React from 'react';

const MyComponent = () => {
  const { data, loading } = useFetch({
    url: "https://jsonplaceholder.typicode.com/posts",
  });

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      <h1>Data from API</h1>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
};

useFetch 커스텀 훅

단일책임원칙에 따라 구현
서비스 로직 불포함
특정 뷰 의존성없이 재사용 가능한 컴포넌트 개발

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

const useFetch = ({ url }) => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(url);
      const list = await res.json();
      setData(list);
      setLoading(false);
    };

    fetchData();
  }, [url]);

  return { data, loading };
};

Error handling

async 함수에서는 비동기 로직을 포함해서, try-catch-finally 처리가 가능하다.

(async function () {
    try {
        const res = await fetch(`http://localhost:3001/milestone2`);
    } catch (e) {
        console.error('exeception occured!!', e.message);
    } finally {
      ...
    }
}

useFetch 에 다양한 상태 반영

  • 3가지를 결과로 반환

    • fetch 결과

    • error 메시지 결과

    • loading 상태

  • response 값이 false라면 response.staus 값을 error 메시지에 담아서 반환

  • 그외 오류에 대해서 catch 로 잡아서, error 메시지를 반환.

useFetch 완성

import { useState, useEffect } from 'react';

const useFetch = ({ url }) => {
  const [data, setData] = useState([]);
  const [errorMsg, setErrorMsg] = useState(''); // 에러 메시지 상태 추가
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await fetch(url);

        // HTTP 응답 상태 확인
        if (!res.ok) {
          setErrorMsg(`Response error. Status code: ${res.status}`);
          return;
        }

        const list = await res.json();
        setData(list);
      } catch (e) {
        console.error('Exception occurred!!', e.message);
        setErrorMsg(e.message); // 예외 발생 시 에러 메시지 설정
      } finally {
        setLoading(false); // 요청 완료 후 로딩 상태 해제
      }
    };

    fetchData();
  }, [url]);

  return { data, errorMsg, loading }; // 에러 메시지 반환
};

export default useFetch;


다양한 hooks

예시)
https://usehooks.com/