Skip to content

Instantly share code, notes, and snippets.

@DarkoKukovec
Created December 27, 2018 14:10
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/eb54c08e20731faab089276abce53a34 to your computer and use it in GitHub Desktop.
Save DarkoKukovec/eb54c08e20731faab089276abce53a34 to your computer and use it in GitHub Desktop.
HOC Component for datx-jsonapi
import { IJsonapiModel, IJsonapiView, Response } from 'datx-jsonapi';
import { IDictionary } from 'datx-utils';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
type LoadingProps = {loading: IDictionary<boolean>} & {
[key: string]: IJsonapiView;
};
interface IResponseWithNext<T extends IJsonapiModel> extends Response<T> {
next: Promise<Response<T>>;
}
function responseHasNext(response: IResponseWithNext<IJsonapiModel>): true;
function responseHasNext(response?: Response<IJsonapiModel>): false;
function responseHasNext(response?: Response<IJsonapiModel> | IResponseWithNext<IJsonapiModel>): boolean {
return Boolean(
response &&
response.pageInfo &&
typeof response.pageInfo.currentPage === 'number' &&
typeof response.pageInfo.totalPages === 'number' &&
response.pageInfo.currentPage < response.pageInfo.totalPages &&
response.next,
);
}
export function infinite(
propName: string,
threshold?: number,
target: HTMLElement = document.body,
) {
return <T, U>(Component: React.ComponentClass<T, U>) => {
@observer
class InfiniteScrollWrapper extends React.Component<T, U> {
private componentState = observable({ loading: false });
public componentWillMount() {
if (typeof threshold === 'number') {
document.addEventListener('scroll', this.onScroll);
this.onScroll();
}
}
public componentWillUnmount() {
if (typeof threshold === 'number') {
document.removeEventListener('scroll', this.onScroll);
}
}
public render() {
const props = {
...this.props,
loading: {
...(this.props as any as LoadingProps).loading,
[propName]: (this.props as any as LoadingProps).loading[propName] || this.componentState.loading,
},
};
this.onScroll();
return (
<Component {...props} loadMore={this.loadMore} />
);
}
@action.bound
private onScroll() {
// TODO: A more robust algorithm
if (target.getBoundingClientRect().bottom - window.innerHeight < 100) {
this.loadMore();
}
}
@action.bound
private loadMore() {
if (!this.componentState.loading) {
const view = (this.props as any as LoadingProps)[propName];
if (responseHasNext(view.latestResponse)) {
this.componentState.loading = true;
(view.latestResponse as IResponseWithNext<IJsonapiModel>).next
.then(() => {
this.componentState.loading = false;
}, () => {
this.componentState.loading = false;
});
}
}
}
}
return InfiniteScrollWrapper as any;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment