Skip to content

Instantly share code, notes, and snippets.

@iamdanthedev
Last active December 2, 2017 11:14
Show Gist options
  • Save iamdanthedev/36c94276cbfa6247f111fbc0fcc68f37 to your computer and use it in GitHub Desktop.
Save iamdanthedev/36c94276cbfa6247f111fbc0fcc68f37 to your computer and use it in GitHub Desktop.
Apollo data loading or error HOC
import * as React from 'react';
import { Loading } from '@common/components/Loading/Loading';
import { ErrorMessage } from '@common/components/ErrorMessage/ErrorMessage';
import { IconSizeProp } from 'semantic-ui-react/src/elements/Icon/Icon';
type Options<T = any> = {
loaderSize?: IconSizeProp,
errorExtraCheck?: (props: T) => string | null | undefined,
loadingExtraCheck?: (props: T) => boolean,
skipLoading?: boolean, // if we want to handle loading state in a wrapped component
skipError?: boolean, // same for error
};
/**
* Check if apollo data is loading or has errored and shows appropriate
* component or the base component otherwise
*/
export function dataLoadingOrError<T extends {}>();
export function dataLoadingOrError<T extends {}>(prop: string | ((props: T) => string));
export function dataLoadingOrError<T extends {}>(options: Options<T>);
export function dataLoadingOrError<T extends {}>(prop: string | ((props: T) => string), options: Options<T>);
export function dataLoadingOrError<T extends {}>() {
const arg0 = arguments[0]; // tslint:disable-line
const arg1 = arguments[1]; // tslint:disable-line
return function(BaseComponent) {
return function(props) {
// args
let dataPropName: string = 'data';
let options: Options = {};
if (typeof arg0 === 'string') {
dataPropName = arg0;
}
else if (typeof arg0 === 'function') {
dataPropName = arg0(props);
}
else if (typeof arg0 === 'object') {
options = Object.assign({}, arg0);
}
if (typeof arg1 === 'object') {
options = Object.assign({}, arg1);
}
if (!!!dataPropName) {
throw new Error('cannot get data prop name');
}
// validate
const data = props[dataPropName];
if (!data) {
console.warn(`data prop "${dataPropName}" not found in props`);
return <BaseComponent {...props} />;
}
// error checks
let error = false;
let errorMessage = '';
if (data.error) {
error = true;
errorMessage = data.error.message;
}
else if (options.errorExtraCheck) {
const extraError = options.errorExtraCheck(props);
if (!!extraError) {
error = true;
errorMessage = extraError;
}
}
// loading checks
let loading = false;
if (data.loading) {
loading = true;
}
else if (options.loadingExtraCheck && options.loadingExtraCheck(props)) {
loading = true;
}
// render
if (loading && !options.skipLoading) {
return <Loading size={options.loaderSize} />;
}
else if (error && !options.skipError) {
return <ErrorMessage message={errorMessage} />;
}
else {
return <BaseComponent {...props} />;
}
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment