1
- import * as React from 'react' ;
2
1
import { RefObject } from '@mui/x-internals/types' ;
3
2
import {
4
3
useGridSelector ,
5
4
useGridEventPriority ,
6
5
gridVisibleColumnDefinitionsSelector ,
7
- useGridApiMethod ,
8
- gridDimensionsSelector ,
6
+ useGridEvent ,
9
7
} from '@mui/x-data-grid' ;
10
- import {
11
- useGridVisibleRows ,
12
- GridInfiniteLoaderPrivateApi ,
13
- useTimeout ,
14
- gridHorizontalScrollbarHeightSelector ,
15
- } from '@mui/x-data-grid/internals' ;
8
+ import { useGridVisibleRows , runIf } from '@mui/x-data-grid/internals' ;
16
9
import useEventCallback from '@mui/utils/useEventCallback' ;
17
- import { styled } from '@mui/system' ;
18
10
import { GridRowScrollEndParams } from '../../../models' ;
19
11
import { GridPrivateApiPro } from '../../../models/gridApiPro' ;
20
12
import { DataGridProProcessedProps } from '../../../models/dataGridProProps' ;
21
13
22
- const InfiniteLoadingTriggerElement = styled ( 'div' ) ( {
23
- position : 'sticky' ,
24
- left : 0 ,
25
- width : 0 ,
26
- height : 0 ,
27
- } ) ;
28
-
29
14
/**
30
15
* @requires useGridColumns (state)
31
16
* @requires useGridDimensions (method) - can be after
@@ -35,110 +20,24 @@ export const useGridInfiniteLoader = (
35
20
apiRef : RefObject < GridPrivateApiPro > ,
36
21
props : Pick <
37
22
DataGridProProcessedProps ,
38
- 'onRowsScrollEnd' | 'pagination' | 'paginationMode' | 'rowsLoadingMode' | 'scrollEndThreshold'
23
+ 'onRowsScrollEnd' | 'pagination' | 'paginationMode' | 'rowsLoadingMode'
39
24
> ,
40
25
) : void => {
41
- const isReady = useGridSelector ( apiRef , gridDimensionsSelector ) . isReady ;
42
26
const visibleColumns = useGridSelector ( apiRef , gridVisibleColumnDefinitionsSelector ) ;
43
27
const currentPage = useGridVisibleRows ( apiRef , props ) ;
44
- const observer = React . useRef < IntersectionObserver > ( null ) ;
45
- const updateTargetTimeout = useTimeout ( ) ;
46
- const triggerElement = React . useRef < HTMLElement | null > ( null ) ;
47
28
48
29
const isEnabled = props . rowsLoadingMode === 'client' && ! ! props . onRowsScrollEnd ;
49
30
50
- const handleLoadMoreRows = useEventCallback ( ( [ entry ] : IntersectionObserverEntry [ ] ) => {
51
- const currentRatio = entry . intersectionRatio ;
52
- const isIntersecting = entry . isIntersecting ;
53
-
54
- if ( isIntersecting && currentRatio === 1 ) {
55
- const viewportPageSize = apiRef . current . getViewportPageSize ( ) ;
56
- const rowScrollEndParams : GridRowScrollEndParams = {
57
- visibleColumns,
58
- viewportPageSize,
59
- visibleRowsCount : currentPage . rows . length ,
60
- } ;
61
- apiRef . current . publishEvent ( 'rowsScrollEnd' , rowScrollEndParams ) ;
62
- observer . current ?. disconnect ( ) ;
63
- // do not observe this node anymore
64
- triggerElement . current = null ;
65
- }
31
+ const handleLoadMoreRows = useEventCallback ( ( ) => {
32
+ const viewportPageSize = apiRef . current . getViewportPageSize ( ) ;
33
+ const rowScrollEndParams : GridRowScrollEndParams = {
34
+ visibleColumns,
35
+ viewportPageSize,
36
+ visibleRowsCount : currentPage . rows . length ,
37
+ } ;
38
+ apiRef . current . publishEvent ( 'rowsScrollEnd' , rowScrollEndParams ) ;
66
39
} ) ;
67
40
68
- React . useEffect ( ( ) => {
69
- const virtualScroller = apiRef . current . virtualScrollerRef . current ;
70
- if ( ! isEnabled || ! isReady || ! virtualScroller ) {
71
- return ;
72
- }
73
- observer . current ?. disconnect ( ) ;
74
-
75
- const horizontalScrollbarHeight = gridHorizontalScrollbarHeightSelector ( apiRef ) ;
76
- const marginBottom = props . scrollEndThreshold - horizontalScrollbarHeight ;
77
-
78
- observer . current = new IntersectionObserver ( handleLoadMoreRows , {
79
- threshold : 1 ,
80
- root : virtualScroller ,
81
- rootMargin : `0px 0px ${ marginBottom } px 0px` ,
82
- } ) ;
83
- if ( triggerElement . current ) {
84
- observer . current . observe ( triggerElement . current ) ;
85
- }
86
- } , [ apiRef , isReady , handleLoadMoreRows , isEnabled , props . scrollEndThreshold ] ) ;
87
-
88
- const updateTarget = ( node : HTMLElement | null ) => {
89
- if ( triggerElement . current !== node ) {
90
- observer . current ?. disconnect ( ) ;
91
-
92
- triggerElement . current = node ;
93
- if ( triggerElement . current ) {
94
- observer . current ?. observe ( triggerElement . current ) ;
95
- }
96
- }
97
- } ;
98
-
99
- const triggerRef = React . useCallback (
100
- ( node : HTMLElement | null ) => {
101
- // Prevent the infite loading working in combination with lazy loading
102
- if ( ! isEnabled ) {
103
- return ;
104
- }
105
-
106
- // If the user scrolls through the grid too fast it might happen that the observer is connected to the trigger element
107
- // that will be intersecting the root inside the same render cycle (but not intersecting at the time of the connection).
108
- // This will cause the observer to not call the callback with `isIntersecting` set to `true`.
109
- // https://www.w3.org/TR/intersection-observer/#event-loop
110
- // Delaying the connection to the next cycle helps since the observer will always call the callback the first time it is connected.
111
- // https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/observe
112
- // Related to
113
- // https://github.com/mui/mui-x/issues/14116
114
- updateTargetTimeout . start ( 0 , ( ) => updateTarget ( node ) ) ;
115
- } ,
116
- [ isEnabled , updateTargetTimeout ] ,
117
- ) ;
118
-
119
- const getInfiniteLoadingTriggerElement = React . useCallback <
120
- NonNullable < GridInfiniteLoaderPrivateApi [ 'getInfiniteLoadingTriggerElement' ] >
121
- > (
122
- ( { lastRowId } ) => {
123
- if ( ! isEnabled ) {
124
- return null ;
125
- }
126
- return (
127
- < InfiniteLoadingTriggerElement
128
- ref = { triggerRef }
129
- // Force rerender on last row change to start observing the new trigger
130
- key = { `trigger-${ lastRowId } ` }
131
- role = "presentation"
132
- />
133
- ) ;
134
- } ,
135
- [ isEnabled , triggerRef ] ,
136
- ) ;
137
-
138
- const infiniteLoaderPrivateApi : GridInfiniteLoaderPrivateApi = {
139
- getInfiniteLoadingTriggerElement,
140
- } ;
141
-
142
- useGridApiMethod ( apiRef , infiniteLoaderPrivateApi , 'private' ) ;
143
41
useGridEventPriority ( apiRef , 'rowsScrollEnd' , props . onRowsScrollEnd ) ;
42
+ useGridEvent ( apiRef , 'onIntersection' , runIf ( isEnabled , handleLoadMoreRows ) ) ;
144
43
} ;
0 commit comments