Skip to content

Instantly share code, notes, and snippets.

@geakstr
Last active December 22, 2018 10:26
Show Gist options
  • Save geakstr/c80c3eb475f1c69168294e3dd36461dc to your computer and use it in GitHub Desktop.
Save geakstr/c80c3eb475f1c69168294e3dd36461dc to your computer and use it in GitHub Desktop.
import * as React from "react";
import { AutoSizer, ScrollEventData, Table } from "react-virtualized";
const CELL_HEIGHT = 18;
const HEADER_HEIGHT = 27;
type Props = any;
// fetch data function
export const fetchData = async (args: {
first: number;
after: number;
last: number;
before: number;
}): Promise<{ data: any; scrollToIndex: number }> => {
const request = async query => await api.query(query);
const data = await request({
first: args.first,
after: args.after,
last: args.last,
before: args.before
});
if (args.before) {
const prependData = await request({
...args,
after: args.before,
before: undefined
});
return {
data: prependData.concat(data),
scrollToIndex: prependData.length
};
}
return {
data,
scrollToIndex: 0
};
};
class TwoWayScroll extends React.Component<Props> {
private width: number = 0;
private height: number = 0;
private tableRef: null | Table;
private disableScroll: boolean;
componentDidMount() {
this.fetch("replace", this.props);
}
fetch = (
mode: "append" | "prepend" | "replace",
props: Props,
callbacks?: {
onSuccess: (result: any) => void;
onError: (error: string) => void;
}
) => {
if (props.filterByParam && props.filterByIdFromParams) {
const numberOfRows = this.numberOfRowsToFetch();
const isAppend = mode === "append";
const isPrepend = mode === "prepend";
const isReplace = mode === "replace";
const first = isAppend || isReplace ? numberOfRows : undefined;
const after = isAppend ? props.endCursor : undefined;
const last = isPrepend ? numberOfRows : undefined;
const before = isPrepend ? props.startCursor : undefined;
this.disableScroll = true;
props.fetchData(
{
first,
after,
last,
before
},
{
onSuccess: (response: any) => {
const { data, scrollToIndex } = response;
if (mode === "prepend") {
if (data.length > 0) {
this.scrollTo(data.length * CELL_HEIGHT + 1);
}
} else if (mode === "replace") {
if (this.tableRef) {
this.scrollTo(scrollToIndex * CELL_HEIGHT);
}
}
if (callbacks && callbacks.onSuccess) {
callbacks.onSuccess(response);
}
this.disableScroll = false;
},
onError: (error: any) => {
if (callbacks && callbacks.onError) {
callbacks.onError(error);
}
this.disableScroll = false;
}
}
);
}
};
numberOfRowsToFetch = () => {
return Math.ceil((window.innerHeight / CELL_HEIGHT) * 1.5);
};
scrollTo = (offset: number) => {
if (this.tableRef && this.height < offset) {
this.tableRef.scrollToPosition(offset);
}
};
onScroll = ({ clientHeight, scrollHeight, scrollTop }: ScrollEventData) => {
if (this.disableScroll) {
return;
} else {
if (scrollHeight - clientHeight === scrollTop) {
this.fetch("append", this.props);
} else if (scrollTop === 0) {
this.fetch("prepend", this.props);
}
}
};
onTableRef = (ref: null | Table) => {
if (ref) {
this.tableRef = ref;
}
};
render() {
const { data } = this.props;
return (
<div className="wrapper">
<AutoSizer>
{({ width, height }) => {
this.width = width;
this.height = height;
return (
<Table
ref={this.onTableRef}
width={this.width}
height={this.height}
onScroll={this.onScroll}
rowHeight={CELL_HEIGHT}
headerHeight={HEADER_HEIGHT}
rowCount={data.length}
overscanRowCount={this.numberOfRowsToFetch()}
scrollingResetTimeInterval={10}
>
...
</Table>
);
}}
</AutoSizer>
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment