Skip to content

Instantly share code, notes, and snippets.

@lesleh
Created January 4, 2022 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lesleh/370452fc744c6345d6f83f492205a6d7 to your computer and use it in GitHub Desktop.
Save lesleh/370452fc744c6345d6f83f492205a6d7 to your computer and use it in GitHub Desktop.
useFetch hook
import { renderHook } from '@testing-library/react-hooks';
import useFetch from '../useFetch';
function mockSuccessfulFetch(data) {
global.fetch = jest.fn().mockResolvedValue({
json: () => Promise.resolve(data),
});
}
function mockErrorFetch(error) {
global.fetch = jest.fn().mockRejectedValue(error);
}
describe('useFetch', () => {
it('should return the initial state', async () => {
mockSuccessfulFetch({});
const { result, waitForNextUpdate } = renderHook(() => useFetch('/'));
expect(result.current).toStrictEqual({
isLoading: true,
isError: false,
data: null,
error: null,
});
await waitForNextUpdate();
});
it('should return the correct data', async () => {
mockSuccessfulFetch('mock data');
const { result, waitForNextUpdate } = renderHook(() => useFetch<string>('http://www.example.com'));
await waitForNextUpdate();
expect(result.current).toStrictEqual({
isLoading: false,
isError: false,
data: 'mock data',
error: null,
});
});
it('should return an error', async () => {
const error = new Error('mock error');
mockErrorFetch(error);
const { result, waitForNextUpdate } = renderHook(() => useFetch<string>('http://www.example.com'));
await waitForNextUpdate();
expect(result.current).toStrictEqual({
isLoading: false,
isError: true,
data: null,
error,
});
});
});
import { useEffect, useReducer } from 'react';
type State<T> = {
isLoading: boolean;
isError: boolean;
data: null | T;
error: null | unknown;
}
type Action<T> = {
type: 'FETCH_INIT',
} | {
type: 'FETCH_SUCCESS',
payload: T;
} | {
type: 'FETCH_ERROR',
payload: unknown;
}
function reducer<T>(state: State<T>, action: Action<T>): State<T> {
switch (action.type) {
case 'FETCH_INIT':
return {
...state,
isLoading: true,
isError: false,
data: null,
error: null,
};
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
isError: false,
data: action.payload,
error: null,
};
case 'FETCH_ERROR':
return {
...state,
isLoading: false,
isError: true,
data: null,
error: action.payload,
};
default:
throw new Error();
}
}
export default function useFetch<T>(url): State<T> {
const [state, dispatch] = useReducer<typeof reducer>(reducer, {
isLoading: true,
isError: false,
data: null,
error: null,
});
const fetchData = async () => {
dispatch({ type: 'FETCH_INIT' });
try {
const response = await fetch(url);
const data = await response.json();
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error });
}
};
useEffect(() => {
fetchData();
}, []);
return state as State<T>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment