[React Project] Custom hook으로 Axios 요청 관리하기

J.A.Y·2024년 3월 29일
0

TypeScript

목록 보기
4/4

React에서는 Axios 요청을 custom hook으로 만들어 관리할 수 있다기에, custom hook 공부도 할 겸 만들어봤습니다.

Fetch로 데이터를 가져오는 경우 비동기로 실행되기 때문에 side effect 관리에 뛰어나다는 useEffect를 사용해서 fetch를 발생시키는 함수가 페이지가 렌더링 될 때마다 호출될 수 있도록 만들어줬습니다.

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

const useFetch = (url: string, method: string) => {
	const [data, setData] = useState<Array<object>>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<boolean>(false);

  if(!url || url.length === 0) {
    console.error('has no url');
    if(!error) setError(true);
    if(loading) setLoading(false);
    return {loading, error}
  }

  const fetchData = async (requestData?: object) => {
    setLoading(true);
    const reqeusetMethod = method === 'POST' ? 'POST' : 'GET';
    
    let requestHeader = {
      method: reqeusetMethod,
      body: JSON.stringify(requestData)
    };

    try {
      const response = await fetch(url, requestHeader);
      //응답값이 있으면 받는 걸로, 없고 전송만 해야 할 때도 있으니까
      const reponseData = await response?.json();
      if (!response.ok) {
        setLoading(false);
        setError(true);
        throw new Error('useFetch.ts에서 reponse 전 예기치 못한 에러 발생')
      } else {
        setData(reponseData);
        setLoading(false);
      }

    } catch (error) {
      setError(true);
      console.log('useFecth.ts 에러 catch: ', error)
    } finally {
      setLoading(false);
    }
  }

  const post = (requestData: object) => {
    if(!requestData) {
      setError(true);
      return 'no reponsed data'
    }
    fetchData(requestData);
  }

  useEffect(() => {
    if (method) return;
    fetchData();
  }, []);

	return { data, loading, error, fetch: fetchData, post};
  
};

export default useFetch;

❗Error❗

그런데 useEffect부분에서 React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. 라는 에러가 발생했습니다.

❓원인❓

알고보니 제가 hook을 조건부로 호출하고 있기 때문이었습니다.
Hook의 규칙 중 하나가 '최상위에서만 호출해야 한다'인데, 그렇지 않으면 hook을 호출하는 순서에 영향을 미쳐 예상하지 못한 버그가 발생하게 된다고 합니다.
그래서 저처럼 조건문, 또는 반복문이나 중첩된 함수 내에서 hook을 호출하면 에러가 발생하게 되는 것입니다.

만약 조건부로 실행시키고 싶다면 어떻게 해야 하나요🤔❓❓

만약 조건부로 실행시키고 싶다면 hook 내부에 조건문을 넣으면 된다고 합니다.

그래서 이번엔 useCallback을 이용해서 fetchData함수를 메모이제션하고, 컴포넌트가 다시 렌더링될 때마다 fetch가 될 수 있도록 해당 함수를 useEffect의 의존성 배열에 추가해줬습니다.

(+ 로딩 상태, 에러의 상태 변화에 따라서도 컴포넌트가 리렌더링 되도록 설계함)

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

const useFetch = (url: string, method: string) => {
  //token 있으면 추가
	const [data, setData] = useState<Array<object>>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<boolean>(false);

  const fetchData = useCallback(async (requestData?: object) => {

    if(!url || url.length === 0) {
      console.error('has no url');
      if(!error) setError(true);
      if(loading) setLoading(false);
      return {loading, error}
    }

    setLoading(true);
    const reqeusetMethod = method === 'POST' ? 'POST' : 'GET';
    let requestHeader = {
      method: reqeusetMethod,
      body: requestData ? JSON.stringify(requestData) : undefined
    };

    try {
      const response = await fetch(url, requestHeader);
      const reponseData = await response.json();
      if (!response.ok) {
        setLoading(false);
        setError(true);
        throw new Error('fail to get data because server response is not ok')
      } 
      setData(reponseData);
    } catch (error) {
      setError(true);
      console.log('fetchData.ts Error:', error)
    } finally {
      setLoading(false);
    }
  }, [url, method, error, loading]);

  const post = useCallback((requestData?: object) => {
    if(!requestData) {
      setError(true);
      return 'faild fetch post because has no request data from client'
    }

    fetchData(requestData);
  },[fetchData])

  useEffect(() => {
    fetchData();
  }, [fetchData]);

	return { data, loading, error, fetch: fetchData, post};
  
};

export default useFetch;

👍코드 실행 문제 없음!

profile
Done is better than perfect🏃‍♀️

0개의 댓글