Skip to content

[기술 공유] useAxios test code

Coa edited this page Mar 1, 2022 · 1 revision

useAxios.test.ts

import { renderHook } from '@testing-library/react-hooks';
import { setupServer } from 'msw/node';
import { rest } from 'msw';

import useAxios from './useAxios';

const stubbedStocks = [
  { symbol: 'AAPL', price: 1 },
  { symbol: 'GOOG', price: 2 },
];
const stubbedFetchUrl = 'api/stocksUrl-mocked';

const server = setupServer();
beforeEach(() => server.resetHandlers());
beforeAll(() => server.listen());
afterAll(() => server.close());

describe('useAxios는', () => {
  it('페치 후에 데이터를 반환해야 한다.', async () => {
    server.use(
      rest.get(stubbedFetchUrl, (req, res, ctx) => {
        return res(ctx.json(stubbedStocks));
      })
    );
    const { result, waitForNextUpdate } = renderHook(() =>
      useAxios(stubbedFetchUrl)
    );
    await waitForNextUpdate();

    expect(result.current).toStrictEqual({
      loading: false,
      data: stubbedStocks,
      error: null,
    });
  });
  it('에러를 캐치해야 한다.', async () => {
    server.use(
      rest.get(stubbedFetchUrl, (req, res, ctx) => {
        return res(
          ctx.status(403),
          ctx.json({ errorMessage: 'Stocks not found' })
        );
      })
    );

    const { result, waitForNextUpdate } = renderHook(() =>
      useAxios(stubbedFetchUrl)
    );
    await waitForNextUpdate();
    console.log(result.current);

    expect(result.current).toStrictEqual({
      loading: false,
      data: null,
      error: 'Stocks not found',
    });
  });
});

useAxios.tsx

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

const axiosConfig = {
  headers: {
    'Content-Type': 'application/json',
    accept: 'text/html; charset=utf-8',
  },
};

interface ReturnType<S> {
  data: S | null;
  loading: boolean;
  error: unknown;
}

function useAxios<S>(url: string): ReturnType<S> {
  const [data, setData] = useState<S | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<unknown>(null);

  const fetchData = async () => {
    try {
      const res = await axios.get(url, axiosConfig);
      if (res.status === 200) {
        setLoading(false);
        setData(res.data);
      } else new Error();
    } catch (e) {
      // check if the error was thrown from axios
      if (axios.isAxiosError(e)) {
        //   // do something
        //   // or just re-throw the error
        //   // ex) throw e;
        setLoading(false);
        setError(e.response ? e.response.data.errorMessage : e.message);
      } else {
        //   // do something else
        //   // or creating a new error
        //   // ex) throw new Error('different error than axios');
        setLoading(false);
        setError('different error');
      }
    }
  };

  useEffect(() => {
    fetchData();
    return () => setLoading(false);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return { data, loading, error };
}

export default useAxios;

useAxios 구현 원리

  • useEffect에서 fetchData를 실행시킨다.
  • fetchData를 처음 실행시키고 난 다음에 다시 useEffect를 만나게 되면? useEffect의 deps = [fetchData]인 상태이기 때문에 다시한번 useEffect된다. 무한 페칭이 걸린다.
  • 따라서 fetchData를 useCallback으로 감싸고 deps = [url]을 주면 fetchData를 호출한 다음 fetchData가 변경되었는지를 판단했을 때 url의 변화가 없으므로 memoization된 fetchData를 기준으로 비교하여 변경되지 않았다고 판단한다.
  • fetchData가 변경되지 않으면 useEffect도 실행되지 않는다.

📝 팀 규칙

🐛 트러블 슈팅

Test 관련 이슈
Next.js 관련 이슈
React 관련 이슈
Git 관련 이슈
기타 이슈

🧑‍💻 개발

기술공유
  • 레퍼런스
  • 📆 회의록

    데일리
    Clone this wiki locally