Skip to content

Instantly share code, notes, and snippets.

@troygoode
Last active May 21, 2021 09:06
Show Gist options
  • Save troygoode/14103bcdb98ecc476c4c3c47ab8c3182 to your computer and use it in GitHub Desktop.
Save troygoode/14103bcdb98ecc476c4c3c47ab8c3182 to your computer and use it in GitHub Desktop.
type-safe Fetch & Re-Fetch with Hooks & TypeScript
import React, { useState } from 'react'
import { getJSON } from './fetch-json'
import useRefreshablePromise from './use-refreshable-promise'
import Async from './async'
interface IProfile {
id: number
name: string
}
const fetchProfiles = async () => {
return getJSON<IProfile[]>('/profiles')
}
const createProfile = async () => {
// TODO: implement
return
}
export default () => {
const [profiles, setProfiles] = useState<IProfile[]>([])
const { refresh, ...fetchState } = useRefreshablePromise(fetchProfiles, setProfiles)
const onCreateProfileClick = async () {
await createProfile()
await refresh() // re-execute fetchProfiles and re-render component
}
return (
<div>
<Async state={fetchState}>
<ul>
{profiles.map((profile) => (
<li key={profile.id}>{profile.name}</li>
)}
</ul>
</Async>
<button onClick={onCreateProfileClick}>Create New Profile</button>
</div>
)
}
import React from 'react'
interface IState {
isLoading: boolean
error?: Error
}
interface IErrorProps {
error: Error
}
interface IAsyncProps {
state: IState
renderError?: React.Component<IErrorProps>
renderLoading?: React.Component<any>
children: React.Component<any>
}
const DefaultErrorComponent = ({ error }: IErrorProps) => {
return <p>ERROR! {error.toString()}</p>
}
const DefaultLoadingComponent = () => {
return <p>Loading...</p>
}
export default ({ state, renderError: ErrorComponent, renderLoading: LoadingComponent, children }: IAsyncProps) => {
const ErrorOverride = ErrorComponent || DefaultErrorComponent
const LoadingOverride = LoadingComponent || DefaultLoadingComponent
if (state.error) {
return <ErrorOverride error={state.error} />
} else if (state.isLoading) {
return <LoadingOverride />
} else {
return children
}
}
// using native browser fetch; just wrapping for clarity
export async function getJSON<T> (url): T {
return (fetch(url).then(res => res.json()) as unknown) as T
}
import { useMemo } from 'react'
import { usePromise } from 'react-hook-utils'
export default <T>(fetchResult: () => Promise<T>, setResult: (result: T) => any) => {
const refresh = async () => {
return fetchResult()
.then((result) => {
setResult(result)
})
}
const fetchMemo = useMemo(refresh, [])
const [, error, isLoading] = usePromise(fetchMemo)
return {
isLoading,
error,
refresh
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment