Last active
September 10, 2019 16:25
-
-
Save melpon/acfc9845a855b439bc374cbd43821d36 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
export type AnyJson = boolean | number | string | null | JsonArray | JsonMap; | |
export interface JsonMap { | |
[key: string]: AnyJson; | |
} | |
// eslint-disable-next-line @typescript-eslint/no-empty-interface | |
export interface JsonArray extends Array<AnyJson> {} | |
type Resolver<T> = (resp: AnyJson | string | Blob | FormData) => T; | |
export function useFetch<T>( | |
mode: "json" | "text" | "blob" | "formData", | |
defaultUrl: string | null, | |
defaultOpts: RequestInit, | |
resolver: Resolver<T>, | |
onError: (error: string) => void | |
): [T | null, number, ((url: string | null, opts: RequestInit) => void)] { | |
const [response, setResponse] = React.useState<T | null>(null); | |
const [counter, setCounter] = React.useState<number>(0); | |
const [fetchCounter, setFetchCounter] = React.useState<number>(0); | |
const [url, setUrl] = React.useState<string | null>(null); | |
const [opts, setOpts] = React.useState<RequestInit>({}); | |
const didMountRef = React.useRef(false); | |
React.useEffect((): (() => void) | undefined => { | |
// 初回は実行しない | |
if (!didMountRef.current) { | |
didMountRef.current = true; | |
return; | |
} | |
const abortController = new AbortController(); | |
(async (): Promise<void> => { | |
if (url === null) { | |
onError("URL が指定されていません"); | |
return; | |
} | |
try { | |
const payload = await fetch(url, { | |
...opts, | |
signal: abortController.signal | |
}); | |
if (Math.floor(payload.status / 100) !== 2) { | |
onError( | |
`${url} リクエストエラー: ステータスコード=${payload.status}` | |
); | |
return; | |
} | |
if (mode === "json") { | |
setResponse(resolver((await payload.json()) as AnyJson)); | |
} else if (mode === "text") { | |
setResponse(resolver(await payload.text())); | |
} else if (mode === "blob") { | |
setResponse(resolver(await payload.blob())); | |
} else if (mode === "formData") { | |
setResponse(resolver(await payload.formData())); | |
} | |
setFetchCounter((counter): number => counter + 1); | |
} catch (error) { | |
onError(`${url} リクエストエラー: エラー=${String(error)}`); | |
} | |
})(); | |
const cleanup = (): void => { | |
abortController.abort(); | |
}; | |
return cleanup; | |
}, [counter]); | |
const doFetch = React.useCallback( | |
(url, opts): void => { | |
setUrl(url || defaultUrl); | |
setOpts(Object.assign(defaultOpts, opts)); | |
setCounter((counter): number => counter + 1); | |
}, | |
[defaultUrl, defaultOpts] | |
); | |
return [response, fetchCounter, doFetch]; | |
} | |
export function useFetchJSON<T>( | |
url: string | null, | |
opts: RequestInit, | |
resolver: (resp: AnyJson) => T, | |
onError: (error: string) => void | |
): [T | null, number, ((url: string | null, opts: RequestInit) => void)] { | |
return useFetch<T>("json", url, opts, resolver as Resolver<T>, onError); | |
} | |
export function useFetchText<T>( | |
url: string | null, | |
opts: RequestInit, | |
resolver: (resp: string) => T, | |
onError: (error: string) => void | |
): [T | null, number, ((url: string | null, opts: RequestInit) => void)] { | |
return useFetch<T>("text", url, opts, resolver as Resolver<T>, onError); | |
} | |
export function useFetchBlob<T>( | |
url: string | null, | |
opts: RequestInit, | |
resolver: (resp: Blob) => T, | |
onError: (error: string) => void | |
): [T | null, number, ((url: string | null, opts: RequestInit) => void)] { | |
return useFetch("blob", url, opts, resolver as Resolver<T>, onError); | |
} | |
export function useFetchFormData<T>( | |
url: string | null, | |
opts: RequestInit, | |
resolver: (resp: FormData) => T, | |
onError: (error: string) => void | |
): [T | null, number, ((url: string | null, opts: RequestInit) => void)] { | |
return useFetch("formData", url, opts, resolver as Resolver<T>, onError); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment