Skip to content

Instantly share code, notes, and snippets.

@DarkoKukovec
Created December 27, 2018 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DarkoKukovec/16e1ba06b85d26ecaa8dfd040382567f to your computer and use it in GitHub Desktop.
Save DarkoKukovec/16e1ba06b85d26ecaa8dfd040382567f to your computer and use it in GitHub Desktop.
HOC Component for datx-jsonapi
import { IModelConstructor, IType, PureModel, View } from 'datx';
import { IJsonapiCollection, IJsonapiModel, IJsonapiView, IRequestOptions, jsonapi, Response } from 'datx-jsonapi';
import { observable, set } from 'mobx';
import * as React from 'react';
import { IDictionary } from '../interfaces/IDictionary';
const JsonapiView = jsonapi(View);
interface ILoaderDefinition {
model: IType | IModelConstructor<IJsonapiModel>;
reqOptions?: IRequestOptions;
options?: {
loadAll: boolean;
};
}
type LoaderInfo = IType | IModelConstructor<IJsonapiModel> | ILoaderDefinition;
type LoadingProps = {loading: IDictionary<boolean>} & {
[key: string]: IJsonapiView;
};
type OriginalComponent = typeof React.Component;
function getLoaderData(loader: LoaderInfo): ILoaderDefinition {
// @ts-ignore
if (typeof loader === 'string' || typeof loader === 'number' || loader.prototype instanceof PureModel) {
return { model: loader as IType | IModelConstructor<IJsonapiModel> };
}
return loader as ILoaderDefinition;
}
async function loadAll(view: IJsonapiView, reqOptions?: IRequestOptions): Promise<Response<IJsonapiModel>> {
let response = await view.fetchPage(1, 1000, reqOptions);
while (
response.pageInfo &&
typeof response.pageInfo.currentPage === 'number' &&
typeof response.pageInfo.totalPages === 'number' &&
response.pageInfo.currentPage < response.pageInfo.totalPages &&
response.next
) {
response = await response.next;
}
return response;
}
export function injectApi(loaders: IDictionary<LoaderInfo>): (Component: OriginalComponent) => any;
// tslint:disable-next-line:max-line-length
export function injectApi(stateProp: string, loaders: IDictionary<LoaderInfo>): (Component: OriginalComponent) => any;
// tslint:disable-next-line:max-line-length
export function injectApi(statePropArg: string | IDictionary<LoaderInfo>, loadersArg?: IDictionary<LoaderInfo>): (Component: OriginalComponent) => any {
const stateProp = typeof statePropArg === 'string' ? statePropArg : 'state';
const loaders = typeof statePropArg === 'string' ? loadersArg as IDictionary<LoaderInfo> : statePropArg;
return <T, U>(Component: React.ComponentClass<T & LoadingProps, U>) => {
const loadingState: LoadingProps = observable({
loading: {},
}) as any;
return class InjectApiComponent extends React.Component<T, U> {
constructor(props: T) {
super(props);
const state = (props as any)[stateProp] as IJsonapiCollection;
Object.keys(loaders).forEach((loader) => {
const { model } = getLoaderData(loaders[loader]);
const loaderView = new JsonapiView(model, state);
set(loadingState, loader, loaderView);
set(loadingState.loading, loader, true);
});
}
public componentDidMount() {
Object.keys(loaders).forEach((loader) => {
const { reqOptions, options } = getLoaderData(loaders[loader]);
set(loadingState.loading, loader, true);
const fetcher = options && options.loadAll
? loadAll(loadingState[loader], reqOptions)
: loadingState[loader].fetchPage(1, undefined, reqOptions);
fetcher.then(() => {
set(loadingState.loading, loader, false);
}, () => {
set(loadingState.loading, loader, false);
});
});
}
public render() {
return (
<Component {...this.props} {...loadingState} />
);
}
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment