React) useApi 커스텀 훅

Clear·2023년 3월 26일
0

개발 노트

목록 보기
4/5

문제상황

api요청 보내는 컴포넌트마다 중복되는 코드가 많아서,
이를 훅으로 만들어 해결하려고 했다.

해결

// useApi.jsx
import { useState } from 'react';
import { useCookies } from 'react-cookie';

function useApi() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const [cookie] = useCookies('accessToken');

  const baseURL = "https://kangwedance.site/dev"

  // [1]. method는 restful api의 메소드, requestBody는 api요청 시 필요한 데이터(객체)
  async function fetchApi(method, url, requestBody) {
    try {
      const response = await fetch(baseURL+url, {
        method: method,
        headers: {
          'Content-Type': 'application/json',
          accesstoken: cookie.accessToken,
        },
        body: JSON.stringify(requestBody)
      });
      const json = await response.json();
      console.log(json)
      setData(json);
    } catch (error) {
      setError(error);
    } finally {
      setIsLoading(false);
    }
  }

  // [2]. api요청할 컴포넌트에서) 요청이 여러개이면, data:childData 이런식으로 꺼내면 된다.
  return { data, isLoading, error, fetchApi };
}

export default useApi;
// ComponentA

...
function ComponentA(props) {
    const dispatch = useDispatch()
    const {data, isLoading, error, fetchApi} = useApi()
    
    useEffect(()=>{
      fetchApi("GET", '/children')
    },[])
    useEffect(()=>{
      if (data){
        dispatch(getChildState(data.data))
      }
    }, [data])
    
    return (
        <Wrapper>
          <ProfileImg/>
        </Wrapper>
    );
}

export default ComponentA;

개선하고 싶었던 부분

response가 반환된 이후의 작업까지 fetchApi의 인자로 넘겨서 처리하도록 하면 훅을 호출한 컴포넌트에서 useEffect의 중복을 줄일 수 있을 것 같았다.

개선된 코드

import { useState, useEffect } from 'react';
import { useCookies } from 'react-cookie';

function useApi() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const [cookie] = useCookies('accessToken');

  const baseURL = "https://kangwedance.site/dev"

  // [1]. method는 restful api의 메소드, requestBody는 api요청 시 필요한 데이터(객체)
  async function fetchApi(method, url, requestBodyOrCallback, onSuccess) {
    // 3번째 인자 body인지 콜백인지 확인
    let requestBody;
    if (typeof requestBodyOrCallback === 'function') onSuccess = requestBodyOrCallback;
    else requestBody = requestBodyOrCallback;

    try {
      const response = await fetch(baseURL+url, {
        method: method,
        headers: {
          'Content-Type': 'application/json',
          accesstoken: cookie.accessToken,
        },
        body: JSON.stringify(requestBody)
      });
      if (!response.ok) throw new Error(`HTTP error: ${response.status}`)
      const json = await response.json();
      console.log(json)
      setData(json);
      if (onSuccess) onSuccess(json)
    } catch (error) {
      setError(error);
      console.error(error)
    } finally {
      setIsLoading(false);
    }
  }

  // [2]. api요청할 컴포넌트에서) 요청이 여러개이면, data:childData 이런식으로 꺼내면 된다.
  return { data, isLoading, error, fetchApi };
}

export default useApi;
// ComponentA

...
function ComponentA(props) {
    const dispatch = useDispatch()
    const {data, isLoading, error, fetchApi} = useApi()
    
    useEffect(()=>{
      fetchApi("GET", '/children', onGetChildStateSuccess)
    },[])
  
  	// fetchApi의 3번쨰 인자로 콜백함수를 넘겨서, 
  	//res가 성공적으로 반환된 이후의 작업을 처리한다.
  	const onGetChildStateSuccess = (json)=>{
      dispatch(getChildState(json.data))
    }
    return (
        <Wrapper>
          <ProfileImg/>
        </Wrapper>
    );
}

export default ComponentA;

0개의 댓글