Skip to content

Instantly share code, notes, and snippets.

@takethefake
Created October 23, 2019 19:49
Show Gist options
  • Save takethefake/c56ed4a4e1465305455355ad448243ff to your computer and use it in GitHub Desktop.
Save takethefake/c56ed4a4e1465305455355ad448243ff to your computer and use it in GitHub Desktop.
import { useEffect, useReducer } from "react";
export interface QueryArgs<T> {
url: string;
options: RequestInit;
deserialize?: (res: Response) => Promise<T>;
}
export interface QueryAction<T> {
type: string;
data: T;
error: Error;
}
export interface QueryState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
const initialState = {
data: null,
loading: false,
error: null
};
const reducer = <T>() => (prevState: QueryState<T>, action: QueryAction<T>) => {
switch (action.type) {
case "START":
return { ...prevState, loading: true };
case "SUCCESS":
return { ...prevState, data: action.data, loading: false };
case "ERROR":
return { ...prevState, error: action.error, loading: false };
default:
throw new Error(`Invalid action type ${action.type}`);
}
};
export const jsonOptions = {
headers: { accept: "application/json" }
};
export function useQuery<T>({
url,
options = {},
deserialize = res => res.json()
}: QueryArgs<T>) {
const typedReducer = reducer<T>();
const [state, dispatch] = useReducer(typedReducer, initialState);
const refetch = async () => {
dispatch({ type: "START" } as QueryAction<T>);
try {
const res = await fetch(url, options);
const jsonData = await deserialize(res);
dispatch({ type: "SUCCESS", data: jsonData } as QueryAction<T>);
} catch (e) {
dispatch({ type: "ERROR", error: e } as QueryAction<T>);
}
};
useEffect(() => {
if (!url) {
return;
}
(async () => {
await refetch();
})();
return () => {};
}, [url, options]);
return { ...state, refetch };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment