Skip to content

Instantly share code, notes, and snippets.

@punmechanic
Last active October 27, 2018 02:40
Show Gist options
  • Save punmechanic/309fccd2b0db424eb9ef1470f322e410 to your computer and use it in GitHub Desktop.
Save punmechanic/309fccd2b0db424eb9ef1470f322e410 to your computer and use it in GitHub Desktop.
Creates generic container components for fetching asynchronous data
import * as React from "react"
interface ChildProps<T, E = any> {
data: T | undefined
error: E | undefined
isFetching: boolean
}
type State<T, E> = ChildProps<T, E>
function makeGenericEffectReducer<T, E, P>(
dispatch: (slice: Partial<State<T, E>>) => void,
fetchData: (props: P) => Promise<T>,
props: P
) {
return async () => {
dispatch({ isFetching: true })
try {
dispatch({ data: await fetchData(props) })
} catch (error) {
dispatch({ error })
} finally {
dispatch({ isFetching: false })
}
}
}
export default function makeContainerComponent<P, T, E = any>(
fetchData: (props: P) => Promise<T>,
initialData?: T,
...propKeys: (keyof P)[]
) {
return (ComponentType: React.ComponentType<Partial<ChildProps<T, E>>>) =>
function GenericContainerComponent(props: P) {
const initialState: ChildProps<T, E> = {
isFetching: false,
error: undefined,
data: initialData
}
const [state, dispatch] = React.useReducer(
(state, slice) => ({ ...state, ...slice }),
initialState
)
React.useEffect(
makeGenericEffectReducer(dispatch, fetchData, props),
propKeys.length > 0 ? propKeys.map(key => props[key]) : undefined
)
return (
<ComponentType
isFetching={state.isFetching}
data={state.data}
error={state.error}
/>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment