Skip to content

Instantly share code, notes, and snippets.

@kn0ll
Last active July 23, 2020 20:46
Show Gist options
  • Save kn0ll/82112d8632bc02976a8bc145474e10b5 to your computer and use it in GitHub Desktop.
Save kn0ll/82112d8632bc02976a8bc145474e10b5 to your computer and use it in GitHub Desktop.
attempts to abstract / manage handling apollo network logic. a strongly typed pattern matching type thing for Apollo

How to Use

  1. with just a ready state, using an anonymous function.
<QueryRenderer query={query}>
    {(data) => (
        <p>{data.hello}</p>
    )}
</QueryRenderer>
  1. with just a ready state, using a component.
const QueryComponent: React.FC<QueryData> = (data) => (
    <p>{data.hello}</p>
);

<QueryRenderer query={query}>
    {QueryComponent}
</QueryRenderer>
  1. overriding custom rendering states, using anonymous functions or components. with this interface, all states besides REQUIRED are optional. (don't worry, the compiler will remind you.) see QueryRendererState for all options.
<QueryRenderer query={query}>
    {{
        [QueryRendererState.EXPECTED_ERROR]: ({ error }) => <p>Custom Error: {error.message}.</p>,
        [QueryRendererState.READY]: QueryComponent,
    }}
</QueryRenderer>
import * as React from 'react';
import { QueryResult } from 'react-apollo';
import { ApolloError } from 'apollo-client';
export enum QueryRendererState {
LOADING,
EXPECTED_ERROR,
UNEXPECTED_ERROR,
READY,
}
type QueryRenderHandler<P = {}> = React.FC<P>;
interface QueryRendererHandlers<D> {
[QueryRendererState.LOADING]: QueryRenderHandler;
[QueryRendererState.EXPECTED_ERROR]: QueryRenderHandler<{ error: ApolloError }>;
[QueryRendererState.UNEXPECTED_ERROR]: QueryRenderHandler;
[QueryRendererState.READY]: QueryRenderHandler<D>;
}
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type QueryRendererHandlersProp<D> = MakeOptional<
QueryRendererHandlers<D>,
QueryRendererState.LOADING | QueryRendererState.EXPECTED_ERROR | QueryRendererState.UNEXPECTED_ERROR
>;
function isChildrenReactComponent<D>(children: QueryRendererHandlersProp<D> | React.FC<D>): children is React.FC<D> {
return typeof children === 'function';
}
const defaultHandlers: Omit<QueryRendererHandlers<undefined>, QueryRendererState.READY> = {
[QueryRendererState.LOADING]: () => <p>Loading...</p>,
[QueryRendererState.EXPECTED_ERROR]: ({ error }) => <p>Expected error occured: {error.message}.</p>,
[QueryRendererState.UNEXPECTED_ERROR]: () => <p>Unexpected error</p>,
};
function QueryRenderer<QueryData, QueryVariables>({
query: { loading, error, data },
children,
}: {
query: QueryResult<QueryData, QueryVariables>;
children: React.FC<QueryData> | QueryRendererHandlersProp<QueryData>;
}): React.ReactElement | null {
const handlers: QueryRendererHandlers<QueryData> = {
...defaultHandlers,
...(isChildrenReactComponent(children) ? { [QueryRendererState.READY]: children } : children),
};
if (loading) {
return handlers[QueryRendererState.LOADING]({});
} else if (error) {
return handlers[QueryRendererState.EXPECTED_ERROR]({ error });
} else if (!data) {
return handlers[QueryRendererState.UNEXPECTED_ERROR]({});
} else {
return handlers[QueryRendererState.READY](data);
}
}
export default QueryRenderer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment