Created
June 10, 2019 06:12
-
-
Save olpeh/334c39dbe0f3f73393b06b437b2bcd2a to your computer and use it in GitHub Desktop.
RemoteData in TypeScript
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 { Status } from './Status'; | |
export const NotAsked: NotAsked = { | |
status: Status.NotAsked | |
}; | |
export interface NotAsked { | |
status: Status.NotAsked; | |
} | |
export const Loading: Loading = { | |
status: Status.Loading | |
}; | |
export interface Loading { | |
status: Status.Loading; | |
} | |
export interface Success<Data> { | |
status: Status.Success; | |
data: Data; | |
} | |
export const ErrorFetching: ErrorFetching = { | |
status: Status.ErrorFetching | |
}; | |
export interface ErrorFetching { | |
status: Status.ErrorFetching; | |
error?: Error; | |
} | |
// Yes, this is valid TypeScript :) | |
export type RemoteData<Data> = | |
| NotAsked | |
| Loading | |
| Success<Data> | |
| ErrorFetching; |
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 { FunctionalComponent, h, ComponentChildren, VNode } from 'preact'; | |
import { RemoteData, Success } from 'models/RemoteData'; | |
import { Card } from 'components/common/Card'; | |
import { Status } from 'models/Status'; | |
import { Spinner } from 'components/common/Spinner'; | |
import { Subtitle } from 'components/common/Subtitle'; | |
import { Title } from 'components/common/Title'; | |
export interface Props { | |
remoteData: RemoteData<unknown>; | |
children: ComponentChildren; | |
// For side-effects | |
onSuccess?: (data: Success<unknown>) => void; | |
renderError?: (error?: Error) => VNode | null; | |
tinySpinner?: boolean; | |
} | |
export const RemoteDataWrapper: FunctionalComponent<Props> = ({ | |
remoteData, | |
children, | |
onSuccess, | |
renderError, | |
tinySpinner | |
}: Props) => { | |
switch (remoteData.status) { | |
case Status.NotAsked: | |
/* | |
* This remote data was not asked yet | |
* Most probably this state is not visible if the fetch happens | |
* in componentDidMount | |
* Most probably we don't even want to display this, so let's hide it | |
* The message may be useful for debugging purposes, though | |
*/ | |
return <span class="visually-hidden">The data has not been fetched yet</span>; | |
case Status.Loading: | |
/* | |
* This remote data is loading but not available yet | |
* let's render a spinner instead | |
*/ | |
return <Spinner tiny={tinySpinner} />; | |
case Status.ErrorFetching: | |
/* | |
* Something went wrong when fetching this remote data | |
* Display the error message to the user | |
* Might not look nice in every place this might be visible | |
*/ | |
if (renderError) { | |
return renderError(remoteData.error); | |
} else { | |
return ( | |
<Card> | |
<Title>Auch! Something went wrong!<Title> | |
{remoteData.error && ( | |
<Subtitle>{remoteData.error.message}</Subtitle> | |
)} | |
<span>Please try again later.<span> | |
</Card> | |
); | |
} | |
case Status.Success: | |
/* | |
* Remote data is now available, so let's render the children | |
* Additionally, call the onSuccess function if defined, because | |
* in some cases we want to do some side-effects, such as updating | |
* the document title when the data is available | |
*/ | |
if (onSuccess) { | |
// Commit horrible side-effects | |
onSuccess(remoteData); | |
} | |
// We use children as the successful state | |
// could have used a render props as well | |
return <div>{children}</div>; | |
default: | |
// Should never happen, but in case this happens, just return null | |
return null; | |
} | |
}; |
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
export enum Status { | |
NotAsked = 'NotAsked', | |
Loading = 'Loading', | |
Success = 'Success', | |
ErrorFetching = 'ErrorFetching' | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment