Skip to content

Commit

Permalink
[DataGridPro] onRowsScrollEnd not firing on very fast scrolling (#14171)
Browse files Browse the repository at this point in the history
Signed-off-by: Armin Mehinovic <[email protected]>
Co-authored-by: Bilal Shafi <[email protected]>
  • Loading branch information
arminmeh and MBilalShafi authored Oct 1, 2024
1 parent 51d0b3a commit 215adb1
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import {
useGridApiMethod,
gridDimensionsSelector,
} from '@mui/x-data-grid';
import { useGridVisibleRows, GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/internals';
import {
useGridVisibleRows,
GridInfiniteLoaderPrivateApi,
useTimeout,
} from '@mui/x-data-grid/internals';
import useEventCallback from '@mui/utils/useEventCallback';
import { styled } from '@mui/system';
import { GridRowScrollEndParams } from '../../../models';
Expand Down Expand Up @@ -35,6 +39,7 @@ export const useGridInfiniteLoader = (
const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector);
const currentPage = useGridVisibleRows(apiRef, props);
const observer = React.useRef<IntersectionObserver>();
const updateTargetTimeout = useTimeout();
const triggerElement = React.useRef<HTMLElement | null>(null);

const isEnabled = props.rowsLoadingMode === 'client' && !!props.onRowsScrollEnd;
Expand Down Expand Up @@ -82,23 +87,35 @@ export const useGridInfiniteLoader = (
}
}, [virtualScroller, handleLoadMoreRows, isEnabled, marginBottom]);

const updateTarget = (node: HTMLElement | null) => {
if (triggerElement.current !== node) {
observer.current?.disconnect();

triggerElement.current = node;
if (triggerElement.current) {
observer.current?.observe(triggerElement.current);
}
}
};

const triggerRef = React.useCallback(
(node: HTMLElement | null) => {
// Prevent the infite loading working in combination with lazy loading
if (!isEnabled) {
return;
}

if (triggerElement.current !== node) {
observer.current?.disconnect();

triggerElement.current = node;
if (triggerElement.current) {
observer.current?.observe(triggerElement.current);
}
}
// If the user scrolls through the grid too fast it might happen that the observer is connected to the trigger element
// that will be intersecting the root inside the same render cycle (but not intersecting at the time of the connection).
// This will cause the observer to not call the callback with `isIntersecting` set to `true`.
// https://www.w3.org/TR/intersection-observer/#event-loop
// Delaying the connection to the next cycle helps since the observer will always call the callback the first time it is connected.
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/observe
// Related to
// https://github.com/mui/mui-x/issues/14116
updateTargetTimeout.start(0, () => updateTarget(node));
},
[isEnabled],
[isEnabled, updateTargetTimeout],
);

const getInfiniteLoadingTriggerElement = React.useCallback<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ describe('<DataGridPro /> - Infnite loader', () => {
// arbitrary number to make sure that the bottom of the grid window is reached.
virtualScroller.scrollTop = 12345;
virtualScroller.dispatchEvent(new Event('scroll'));
// wait for the next render cycle
await sleep(0);
// observer was attached
expect(observe.callCount).to.equal(1);
});
Expand Down

0 comments on commit 215adb1

Please sign in to comment.