Skip to content

Commit

Permalink
fix: react table virtual scroll (#1879)
Browse files Browse the repository at this point in the history
* fix: react table virtual scroll

close #1863
  • Loading branch information
hamed-musallam committed Nov 10, 2022
1 parent a55ab6f commit 8b6def9
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 58 deletions.
96 changes: 40 additions & 56 deletions src/component/elements/ReactTable/ReactTable.tsx
Expand Up @@ -2,14 +2,13 @@
/** @jsxImportSource @emotion/react */
import {
useRef,
useCallback,
memo,
forwardRef,
useState,
Ref,
useEffect,
UIEvent,
CSSProperties,
WheelEvent,
useLayoutEffect,
} from 'react';
import { useTable, useSortBy } from 'react-table';
import { useMeasure } from 'react-use';
Expand Down Expand Up @@ -41,7 +40,7 @@ interface ReactTableProps extends ClickEvent {
}

interface ReactTableInnerProps extends ReactTableProps {
onScroll: (event: UIEvent<HTMLDivElement>) => void;
onScroll: (event: WheelEvent<HTMLDivElement>) => void;
}

const styles = {
Expand Down Expand Up @@ -84,7 +83,7 @@ const ReactTableInner = forwardRef(function ReactTableInner(
} = props;

const contextRef = useRef<any>(null);
const { index: indexBoundary } = useReactTableContext();
const virtualBoundary = useReactTableContext();
const [rowIndex, setRowIndex] = useState<number>();
const timeoutIdRef = useRef<NodeJS.Timeout>();
const [isCounterVisible, setCounterVisibility] = useState(false);
Expand All @@ -104,27 +103,17 @@ const ReactTableInner = forwardRef(function ReactTableInner(
useSortBy,
useRowSpan,
);
const contextMenuHandler = useCallback(
(e, row) => {
if (!checkModifierKeyActivated(e)) {
e.preventDefault();
contextRef.current.handleContextMenu(e, row.original);
}
},
[contextRef],
);

const rowsData = enableVirtualScroll
? rows.slice(indexBoundary.start, indexBoundary.end)
: rows;
function contextMenuHandler(e, row) {
if (!checkModifierKeyActivated(e)) {
e.preventDefault();
contextRef.current.handleContextMenu(e, row.original);
}
}

const clickHandler = useCallback(
(event, row) => {
setRowIndex(row.index);
onClick?.(event, row);
},
[onClick],
);
function clickHandler(event, row) {
setRowIndex(row.index);
onClick?.(event, row);
}

function scrollHandler(e) {
if (enableVirtualScroll) {
Expand All @@ -141,6 +130,14 @@ const ReactTableInner = forwardRef(function ReactTableInner(
}, 1000);
}

const end =
virtualBoundary.end === rows.length - 1
? virtualBoundary.end + 1
: virtualBoundary.end;
const rowsData = enableVirtualScroll
? rows.slice(virtualBoundary.start, end)
: rows;

const index =
rowsData[rowsData.length - 1]?.original[indexKey] ||
rowsData[rowsData.length - 1]?.index;
Expand All @@ -161,7 +158,7 @@ const ReactTableInner = forwardRef(function ReactTableInner(
{enableVirtualScroll && (
<div
style={{
height: approxItemHeight * data.length,
height: approxItemHeight * (data.length + 1),
position: 'absolute',
width: '100%',
pointerEvents: 'none',
Expand All @@ -180,7 +177,7 @@ const ReactTableInner = forwardRef(function ReactTableInner(

prepareRowSpan(
rows,
enableVirtualScroll ? index + indexBoundary.start : index,
enableVirtualScroll ? index + virtualBoundary.start : index,
rowSpanHeaders,
groupKey,
);
Expand Down Expand Up @@ -217,37 +214,30 @@ const ReactTableInner = forwardRef(function ReactTableInner(
);
});

export interface TableVirtualConfig {
scrollHeight: number;
numberOfVisibleRows: number;
index: { start: number; end: number };
export interface TableVirtualBoundary {
start: number;
end: number;
}

function ReactTable(props: ReactTableProps) {
const { data, approxItemHeight = 40, groupKey } = props;
const containerRef = useRef<HTMLDivElement | null>(null);
const visibleRowsCountRef = useRef<number>(0);
const [mRef, { height }] = useMeasure<HTMLDivElement>();

const [tableVirtualConfig, setTableVirtualConfig] =
useState<TableVirtualConfig>({
scrollHeight: 0,
numberOfVisibleRows: 0,
index: { start: 0, end: 0 },
const [tableVirtualBoundary, setTableVirtualBoundary] =
useState<TableVirtualBoundary>({
start: 1,
end: 0,
});

useEffect(() => {
useLayoutEffect(() => {
if (containerRef.current) {
const { scrollHeight } = containerRef.current;
const header = containerRef.current.querySelectorAll('thead');
const numberOfVisibleRows = Math.ceil(
visibleRowsCountRef.current = Math.ceil(
(height - header[0].clientHeight) / approxItemHeight,
);
setTableVirtualConfig((prev) => ({
...prev,
scrollHeight,
numberOfVisibleRows: numberOfVisibleRows - 1,
index: { start: 0, end: numberOfVisibleRows },
}));
setTableVirtualBoundary({ start: 0, end: visibleRowsCountRef.current });
}
}, [approxItemHeight, height]);

Expand Down Expand Up @@ -300,23 +290,17 @@ function ReactTable(props: ReactTableProps) {
}

function scrollHandler() {
if (containerRef.current && tableVirtualConfig) {
if (containerRef.current) {
const { scrollTop } = containerRef.current;
const { numberOfVisibleRows, index } = tableVirtualConfig;
const currentIndx = Math.ceil(scrollTop / approxItemHeight);
const start = findStartIndex(currentIndx, numberOfVisibleRows);
if (currentIndx !== index.start) {
const end = findEndIndex(currentIndx, numberOfVisibleRows);
setTableVirtualConfig({
...tableVirtualConfig,
index: { start, end: end + 1 },
});
}
const start = findStartIndex(currentIndx, visibleRowsCountRef.current);
const end = findEndIndex(currentIndx, visibleRowsCountRef.current);
setTableVirtualBoundary({ start, end });
}
}

return (
<ReactTableProvider value={tableVirtualConfig}>
<ReactTableProvider value={tableVirtualBoundary}>
<div
ref={mRef}
style={{
Expand Down
@@ -1,8 +1,8 @@
import { createContext, useContext } from 'react';

import { TableVirtualConfig } from '../ReactTable';
import { TableVirtualBoundary } from '../ReactTable';

const reactContext = createContext<TableVirtualConfig | null>(null);
const reactContext = createContext<TableVirtualBoundary | null>(null);
export const ReactTableProvider = reactContext.Provider;

export function useReactTableContext() {
Expand Down

0 comments on commit 8b6def9

Please sign in to comment.