virtual-scroller 1.7.9 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/.gitlab-ci.yml +1 -1
  2. package/CHANGELOG.md +71 -1
  3. package/README.md +434 -151
  4. package/bundle/index-bypass.html +1 -1
  5. package/bundle/index-dom.html +1 -1
  6. package/bundle/index-grid.html +1 -2
  7. package/bundle/index-scrollableContainer.html +1 -1
  8. package/bundle/index-tbody-scrollableContainer.html +2 -0
  9. package/bundle/index-tbody.html +2 -0
  10. package/bundle/virtual-scroller-dom.js +1 -1
  11. package/bundle/virtual-scroller-dom.js.map +1 -1
  12. package/bundle/virtual-scroller-react.js +1 -1
  13. package/bundle/virtual-scroller-react.js.map +1 -1
  14. package/bundle/virtual-scroller.js +1 -1
  15. package/bundle/virtual-scroller.js.map +1 -1
  16. package/commonjs/BeforeResize.js +315 -0
  17. package/commonjs/BeforeResize.js.map +1 -0
  18. package/commonjs/DOM/Engine.js +46 -0
  19. package/commonjs/DOM/Engine.js.map +1 -0
  20. package/commonjs/DOM/ItemsContainer.js +78 -0
  21. package/commonjs/DOM/ItemsContainer.js.map +1 -0
  22. package/commonjs/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +71 -44
  23. package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -0
  24. package/commonjs/DOM/ScrollableContainer.js +69 -101
  25. package/commonjs/DOM/ScrollableContainer.js.map +1 -1
  26. package/commonjs/DOM/VirtualScroller.js +37 -29
  27. package/commonjs/DOM/VirtualScroller.js.map +1 -1
  28. package/commonjs/DOM/tbody.js +17 -11
  29. package/commonjs/DOM/tbody.js.map +1 -1
  30. package/commonjs/ItemHeights.js +33 -34
  31. package/commonjs/ItemHeights.js.map +1 -1
  32. package/commonjs/Layout.js +591 -216
  33. package/commonjs/Layout.js.map +1 -1
  34. package/commonjs/Layout.test.js +196 -0
  35. package/commonjs/Layout.test.js.map +1 -0
  36. package/commonjs/ListHeightMeasurement.js +124 -0
  37. package/commonjs/ListHeightMeasurement.js.map +1 -0
  38. package/commonjs/Resize.js +50 -39
  39. package/commonjs/Resize.js.map +1 -1
  40. package/commonjs/Scroll.js +139 -95
  41. package/commonjs/Scroll.js.map +1 -1
  42. package/commonjs/VirtualScroller.columns.js +43 -0
  43. package/commonjs/VirtualScroller.columns.js.map +1 -0
  44. package/commonjs/VirtualScroller.constructor.js +408 -0
  45. package/commonjs/VirtualScroller.constructor.js.map +1 -0
  46. package/commonjs/VirtualScroller.items.js +305 -0
  47. package/commonjs/VirtualScroller.items.js.map +1 -0
  48. package/commonjs/VirtualScroller.js +160 -1021
  49. package/commonjs/VirtualScroller.js.map +1 -1
  50. package/commonjs/VirtualScroller.layout.js +562 -0
  51. package/commonjs/VirtualScroller.layout.js.map +1 -0
  52. package/commonjs/VirtualScroller.onRender.js +357 -0
  53. package/commonjs/VirtualScroller.onRender.js.map +1 -0
  54. package/commonjs/VirtualScroller.resize.js +186 -0
  55. package/commonjs/VirtualScroller.resize.js.map +1 -0
  56. package/commonjs/VirtualScroller.state.js +301 -0
  57. package/commonjs/VirtualScroller.state.js.map +1 -0
  58. package/commonjs/VirtualScroller.verticalSpacing.js +65 -0
  59. package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -0
  60. package/commonjs/getItemCoordinates.js.map +1 -1
  61. package/commonjs/getItemsDiff.js.map +1 -1
  62. package/commonjs/getVerticalSpacing.js +8 -8
  63. package/commonjs/getVerticalSpacing.js.map +1 -1
  64. package/commonjs/package.json +5 -0
  65. package/commonjs/react/VirtualScroller.js +182 -628
  66. package/commonjs/react/VirtualScroller.js.map +1 -1
  67. package/commonjs/react/useClassName.js +26 -0
  68. package/commonjs/react/useClassName.js.map +1 -0
  69. package/commonjs/react/useHandleItemsChange.js +116 -0
  70. package/commonjs/react/useHandleItemsChange.js.map +1 -0
  71. package/commonjs/react/useInstanceMethods.js +37 -0
  72. package/commonjs/react/useInstanceMethods.js.map +1 -0
  73. package/commonjs/react/useItemKeys.js +60 -0
  74. package/commonjs/react/useItemKeys.js.map +1 -0
  75. package/commonjs/react/useOnItemHeightChange.js +32 -0
  76. package/commonjs/react/useOnItemHeightChange.js.map +1 -0
  77. package/commonjs/react/useOnItemStateChange.js +32 -0
  78. package/commonjs/react/useOnItemStateChange.js.map +1 -0
  79. package/commonjs/react/useState.js +140 -0
  80. package/commonjs/react/useState.js.map +1 -0
  81. package/commonjs/react/useStyle.js +29 -0
  82. package/commonjs/react/useStyle.js.map +1 -0
  83. package/commonjs/react/useVirtualScroller.js +62 -0
  84. package/commonjs/react/useVirtualScroller.js.map +1 -0
  85. package/commonjs/react/useVirtualScrollerStartStop.js +20 -0
  86. package/commonjs/react/useVirtualScrollerStartStop.js.map +1 -0
  87. package/commonjs/test/Engine.js +23 -0
  88. package/commonjs/test/Engine.js.map +1 -0
  89. package/commonjs/test/ItemsContainer.js +127 -0
  90. package/commonjs/test/ItemsContainer.js.map +1 -0
  91. package/commonjs/test/ScrollableContainer.js +130 -0
  92. package/commonjs/test/ScrollableContainer.js.map +1 -0
  93. package/commonjs/test/VirtualScroller.js +281 -0
  94. package/commonjs/test/VirtualScroller.js.map +1 -0
  95. package/commonjs/utility/debounce.js +28 -6
  96. package/commonjs/utility/debounce.js.map +1 -1
  97. package/commonjs/utility/debug.js +51 -12
  98. package/commonjs/utility/debug.js.map +1 -1
  99. package/commonjs/utility/getStateSnapshot.js +50 -0
  100. package/commonjs/utility/getStateSnapshot.js.map +1 -0
  101. package/commonjs/utility/px.js +1 -1
  102. package/commonjs/utility/px.js.map +1 -1
  103. package/commonjs/utility/px.test.js +14 -0
  104. package/commonjs/utility/px.test.js.map +1 -0
  105. package/commonjs/utility/shallowEqual.js +1 -1
  106. package/commonjs/utility/shallowEqual.js.map +1 -1
  107. package/commonjs/utility/throttle.js.map +1 -1
  108. package/dom/index.cjs +4 -0
  109. package/dom/index.cjs.js +9 -0
  110. package/dom/index.d.ts +25 -0
  111. package/dom/index.js +1 -1
  112. package/dom/package.json +10 -4
  113. package/index.cjs +4 -0
  114. package/index.cjs.js +9 -0
  115. package/index.d.ts +99 -0
  116. package/index.js +1 -1
  117. package/modules/BeforeResize.js +305 -0
  118. package/modules/BeforeResize.js.map +1 -0
  119. package/modules/DOM/Engine.js +27 -0
  120. package/modules/DOM/Engine.js.map +1 -0
  121. package/modules/DOM/ItemsContainer.js +71 -0
  122. package/modules/DOM/ItemsContainer.js.map +1 -0
  123. package/modules/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +72 -44
  124. package/modules/DOM/ListTopOffsetWatcher.js.map +1 -0
  125. package/modules/DOM/ScrollableContainer.js +68 -100
  126. package/modules/DOM/ScrollableContainer.js.map +1 -1
  127. package/modules/DOM/VirtualScroller.js +32 -28
  128. package/modules/DOM/VirtualScroller.js.map +1 -1
  129. package/modules/DOM/tbody.js +11 -9
  130. package/modules/DOM/tbody.js.map +1 -1
  131. package/modules/ItemHeights.js +28 -33
  132. package/modules/ItemHeights.js.map +1 -1
  133. package/modules/Layout.js +585 -214
  134. package/modules/Layout.js.map +1 -1
  135. package/modules/Layout.test.js +190 -0
  136. package/modules/Layout.test.js.map +1 -0
  137. package/modules/ListHeightMeasurement.js +117 -0
  138. package/modules/ListHeightMeasurement.js.map +1 -0
  139. package/modules/Resize.js +50 -39
  140. package/modules/Resize.js.map +1 -1
  141. package/modules/Scroll.js +139 -94
  142. package/modules/Scroll.js.map +1 -1
  143. package/modules/VirtualScroller.columns.js +36 -0
  144. package/modules/VirtualScroller.columns.js.map +1 -0
  145. package/modules/VirtualScroller.constructor.js +371 -0
  146. package/modules/VirtualScroller.constructor.js.map +1 -0
  147. package/modules/VirtualScroller.items.js +288 -0
  148. package/modules/VirtualScroller.items.js.map +1 -0
  149. package/modules/VirtualScroller.js +159 -1014
  150. package/modules/VirtualScroller.js.map +1 -1
  151. package/modules/VirtualScroller.layout.js +549 -0
  152. package/modules/VirtualScroller.layout.js.map +1 -0
  153. package/modules/VirtualScroller.onRender.js +337 -0
  154. package/modules/VirtualScroller.onRender.js.map +1 -0
  155. package/modules/VirtualScroller.resize.js +176 -0
  156. package/modules/VirtualScroller.resize.js.map +1 -0
  157. package/modules/VirtualScroller.state.js +283 -0
  158. package/modules/VirtualScroller.state.js.map +1 -0
  159. package/modules/VirtualScroller.verticalSpacing.js +54 -0
  160. package/modules/VirtualScroller.verticalSpacing.js.map +1 -0
  161. package/modules/getItemCoordinates.js.map +1 -1
  162. package/modules/getItemsDiff.js.map +1 -1
  163. package/modules/getVerticalSpacing.js +8 -8
  164. package/modules/getVerticalSpacing.js.map +1 -1
  165. package/modules/react/VirtualScroller.js +179 -634
  166. package/modules/react/VirtualScroller.js.map +1 -1
  167. package/modules/react/useClassName.js +18 -0
  168. package/modules/react/useClassName.js.map +1 -0
  169. package/modules/react/useHandleItemsChange.js +108 -0
  170. package/modules/react/useHandleItemsChange.js.map +1 -0
  171. package/modules/react/useInstanceMethods.js +28 -0
  172. package/modules/react/useInstanceMethods.js.map +1 -0
  173. package/modules/react/useItemKeys.js +52 -0
  174. package/modules/react/useItemKeys.js.map +1 -0
  175. package/modules/react/useOnItemHeightChange.js +24 -0
  176. package/modules/react/useOnItemHeightChange.js.map +1 -0
  177. package/modules/react/useOnItemStateChange.js +24 -0
  178. package/modules/react/useOnItemStateChange.js.map +1 -0
  179. package/modules/react/useState.js +132 -0
  180. package/modules/react/useState.js.map +1 -0
  181. package/modules/react/useStyle.js +19 -0
  182. package/modules/react/useStyle.js.map +1 -0
  183. package/modules/react/useVirtualScroller.js +51 -0
  184. package/modules/react/useVirtualScroller.js.map +1 -0
  185. package/modules/react/useVirtualScrollerStartStop.js +12 -0
  186. package/modules/react/useVirtualScrollerStartStop.js.map +1 -0
  187. package/modules/test/Engine.js +11 -0
  188. package/modules/test/Engine.js.map +1 -0
  189. package/modules/test/ItemsContainer.js +120 -0
  190. package/modules/test/ItemsContainer.js.map +1 -0
  191. package/modules/test/ScrollableContainer.js +123 -0
  192. package/modules/test/ScrollableContainer.js.map +1 -0
  193. package/modules/test/VirtualScroller.js +270 -0
  194. package/modules/test/VirtualScroller.js.map +1 -0
  195. package/modules/utility/debounce.js +28 -6
  196. package/modules/utility/debounce.js.map +1 -1
  197. package/modules/utility/debug.js +47 -10
  198. package/modules/utility/debug.js.map +1 -1
  199. package/modules/utility/getStateSnapshot.js +43 -0
  200. package/modules/utility/getStateSnapshot.js.map +1 -0
  201. package/modules/utility/px.js +1 -1
  202. package/modules/utility/px.js.map +1 -1
  203. package/modules/utility/px.test.js +9 -0
  204. package/modules/utility/px.test.js.map +1 -0
  205. package/modules/utility/shallowEqual.js +1 -1
  206. package/modules/utility/shallowEqual.js.map +1 -1
  207. package/modules/utility/throttle.js.map +1 -1
  208. package/package.json +54 -29
  209. package/react/index.cjs +4 -0
  210. package/react/index.cjs.js +9 -0
  211. package/react/index.d.ts +28 -0
  212. package/react/index.js +1 -1
  213. package/react/package.json +10 -4
  214. package/rollup.config.mjs +62 -0
  215. package/runnable/create-commonjs-package-json.js +11 -0
  216. package/source/BeforeResize.js +312 -0
  217. package/source/DOM/Engine.js +30 -0
  218. package/source/DOM/ItemsContainer.js +48 -0
  219. package/source/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +61 -30
  220. package/source/DOM/ScrollableContainer.js +51 -73
  221. package/source/DOM/VirtualScroller.js +33 -18
  222. package/source/DOM/tbody.js +30 -21
  223. package/source/ItemHeights.js +27 -27
  224. package/source/Layout.js +629 -252
  225. package/source/Layout.test.js +176 -0
  226. package/source/ListHeightMeasurement.js +95 -0
  227. package/source/Resize.js +56 -32
  228. package/source/Scroll.js +135 -82
  229. package/source/VirtualScroller.columns.js +26 -0
  230. package/source/VirtualScroller.constructor.js +336 -0
  231. package/source/VirtualScroller.items.js +302 -0
  232. package/source/VirtualScroller.js +162 -936
  233. package/source/VirtualScroller.layout.js +539 -0
  234. package/source/VirtualScroller.onRender.js +345 -0
  235. package/source/VirtualScroller.resize.js +189 -0
  236. package/source/VirtualScroller.state.js +284 -0
  237. package/source/VirtualScroller.verticalSpacing.js +51 -0
  238. package/source/getVerticalSpacing.js +7 -7
  239. package/source/react/VirtualScroller.js +243 -603
  240. package/source/react/useClassName.js +14 -0
  241. package/source/react/useHandleItemsChange.js +115 -0
  242. package/source/react/useInstanceMethods.js +25 -0
  243. package/source/react/useItemKeys.js +59 -0
  244. package/source/react/useOnItemHeightChange.js +28 -0
  245. package/source/react/useOnItemStateChange.js +28 -0
  246. package/source/react/useState.js +114 -0
  247. package/source/react/useStyle.js +20 -0
  248. package/source/react/useVirtualScroller.js +59 -0
  249. package/source/react/useVirtualScrollerStartStop.js +12 -0
  250. package/source/test/Engine.js +11 -0
  251. package/source/test/ItemsContainer.js +87 -0
  252. package/source/test/ScrollableContainer.js +88 -0
  253. package/source/test/VirtualScroller.js +232 -0
  254. package/source/utility/debounce.js +22 -5
  255. package/source/utility/debug.js +34 -3
  256. package/source/utility/getStateSnapshot.js +36 -0
  257. package/source/utility/px.js +1 -1
  258. package/source/utility/px.test.js +9 -0
  259. package/website/index-bypass.html +195 -0
  260. package/website/index-grid.html +0 -1
  261. package/website/index-scrollableContainer.html +208 -0
  262. package/website/index-tbody-scrollableContainer.html +68 -0
  263. package/website/index-tbody.html +55 -0
  264. package/commonjs/DOM/RenderingEngine.js +0 -33
  265. package/commonjs/DOM/RenderingEngine.js.map +0 -1
  266. package/commonjs/DOM/Screen.js +0 -87
  267. package/commonjs/DOM/Screen.js.map +0 -1
  268. package/commonjs/DOM/WaitForStylesToLoad.js.map +0 -1
  269. package/commonjs/RestoreScroll.js +0 -118
  270. package/commonjs/RestoreScroll.js.map +0 -1
  271. package/dom/index.commonjs.js +0 -4
  272. package/index.commonjs.js +0 -4
  273. package/modules/DOM/RenderingEngine.js +0 -19
  274. package/modules/DOM/RenderingEngine.js.map +0 -1
  275. package/modules/DOM/Screen.js +0 -80
  276. package/modules/DOM/Screen.js.map +0 -1
  277. package/modules/DOM/WaitForStylesToLoad.js.map +0 -1
  278. package/modules/RestoreScroll.js +0 -111
  279. package/modules/RestoreScroll.js.map +0 -1
  280. package/react/index.commonjs.js +0 -4
  281. package/source/DOM/RenderingEngine.js +0 -22
  282. package/source/DOM/Screen.js +0 -51
  283. package/source/RestoreScroll.js +0 -86
@@ -1,619 +1,259 @@
1
- import React from 'react'
1
+ import { useRef, useMemo, useLayoutEffect } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
- import VirtualScrollerCore from '../VirtualScroller'
5
-
6
- import { reportError } from '../utility/debug'
7
- import px from '../utility/px'
8
-
9
- // `PropTypes.elementType` is available in some version of `prop-types`.
10
- // https://github.com/facebook/prop-types/issues/200
11
- const elementType = PropTypes.elementType || PropTypes.oneOfType([
12
- PropTypes.string,
13
- PropTypes.func,
14
- PropTypes.object
15
- ])
16
-
17
- export default class VirtualScroller extends React.Component {
18
- static propTypes = {
19
- as: elementType,
20
- items: PropTypes.arrayOf(PropTypes.object).isRequired,
21
- itemComponent: elementType.isRequired,
22
- itemComponentProps: PropTypes.object,
23
- estimatedItemHeight: PropTypes.number,
24
- bypass: PropTypes.bool,
25
- // bypassBatchSize: PropTypes.number,
26
- preserveScrollPositionOnPrependItems: PropTypes.bool,
27
- // `preserveScrollPosition` property name is deprecated,
28
- // use `preserveScrollPositionOnPrependItems` instead.
29
- preserveScrollPosition: PropTypes.bool,
30
- preserveScrollPositionOfTheBottomOfTheListOnMount: PropTypes.bool,
31
- // `preserveScrollPositionAtBottomOnMount` property name is deprecated,
32
- // use `preserveScrollPositionOfTheBottomOfTheListOnMount` property instead.
33
- preserveScrollPositionAtBottomOnMount: PropTypes.bool,
34
- measureItemsBatchSize: PropTypes.number,
35
- scrollableContainer: PropTypes.any,
36
- // `getScrollableContainer` property is deprecated.
37
- // Use `scrollableContainer` instead.
38
- getScrollableContainer: PropTypes.func,
39
- getColumnsCount: PropTypes.func,
40
- getItemId: PropTypes.func,
41
- className: PropTypes.string,
42
- onMount: PropTypes.func,
43
- onItemInitialRender: PropTypes.func,
4
+ import useState from './useState.js'
5
+ import useVirtualScroller from './useVirtualScroller.js'
6
+ import useVirtualScrollerStartStop from './useVirtualScrollerStartStop.js'
7
+ import useInstanceMethods from './useInstanceMethods.js'
8
+ import useItemKeys from './useItemKeys.js'
9
+ import useOnItemStateChange from './useOnItemStateChange.js'
10
+ import useOnItemHeightChange from './useOnItemHeightChange.js'
11
+ import useHandleItemsChange from './useHandleItemsChange.js'
12
+ import useClassName from './useClassName.js'
13
+ import useStyle from './useStyle.js'
14
+
15
+ function VirtualScroller({
16
+ as: AsComponent,
17
+ items,
18
+ itemComponent: Component,
19
+ itemComponentProps,
20
+ estimatedItemHeight,
21
+ bypass,
22
+ tbody,
23
+ // `preserveScrollPosition` property name is deprecated,
24
+ // use `preserveScrollPositionOnPrependItems` property instead.
25
+ preserveScrollPosition,
26
+ preserveScrollPositionOnPrependItems,
27
+ measureItemsBatchSize,
28
+ // `scrollableContainer` property is deprecated.
29
+ // Use `getScrollableContainer()` property instead.
30
+ scrollableContainer,
31
+ getScrollableContainer,
32
+ getColumnsCount,
33
+ getItemId,
34
+ className,
35
+ onMount,
36
+ // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
37
+ onItemFirstRender,
38
+ onItemInitialRender,
39
+ initialScrollPosition,
40
+ onScrollPositionChange,
41
+ onStateChange,
42
+ initialState,
43
+ ...rest
44
+ }, ref) {
45
+ // List items "container" DOM Element reference.
46
+ const container = useRef()
47
+
48
+ // Create a `VirtualScroller` instance.
49
+ const virtualScroller = useVirtualScroller({
50
+ items,
51
+ estimatedItemHeight,
52
+ bypass,
53
+ // bypassBatchSize,
54
+ tbody,
55
+ onItemInitialRender,
44
56
  // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
45
- onItemFirstRender: PropTypes.func,
46
- initialScrollPosition: PropTypes.number,
47
- onScrollPositionChange: PropTypes.func,
48
- onStateChange: PropTypes.func,
49
- initialCustomState: PropTypes.object,
50
- initialState: PropTypes.shape({
51
- items: PropTypes.arrayOf(PropTypes.object).isRequired,
52
- itemStates: PropTypes.arrayOf(PropTypes.any),
53
- firstShownItemIndex: PropTypes.number.isRequired,
54
- lastShownItemIndex: PropTypes.number.isRequired,
55
- beforeItemsHeight: PropTypes.number.isRequired,
56
- afterItemsHeight: PropTypes.number.isRequired,
57
- itemHeights: PropTypes.arrayOf(PropTypes.number).isRequired,
58
- columnsCount: PropTypes.number,
59
- verticalSpacing: PropTypes.number
57
+ onItemFirstRender,
58
+ initialScrollPosition,
59
+ onScrollPositionChange,
60
+ measureItemsBatchSize,
61
+ // `scrollableContainer` property is deprecated.
62
+ // Use `getScrollableContainer()` property instead.
63
+ scrollableContainer,
64
+ getScrollableContainer,
65
+ getColumnsCount,
66
+ getItemId,
67
+ AsComponent,
68
+ initialState,
69
+ onStateChange
70
+ }, {
71
+ container
72
+ })
73
+
74
+ // Only compute the initial state once.
75
+ const _initialState = useMemo(() => {
76
+ return virtualScroller.getInitialState()
77
+ }, [])
78
+
79
+ // Create state management functions.
80
+ const {
81
+ getState,
82
+ updateState
83
+ } = useState({
84
+ initialState: _initialState,
85
+ onRender: virtualScroller.onRender,
86
+ items
87
+ })
88
+
89
+ // Use custom (external) state storage in the `VirtualScroller`.
90
+ useMemo(() => {
91
+ virtualScroller.useState({
92
+ getState,
93
+ updateState
60
94
  })
61
- }
62
-
63
- static defaultProps = {
64
- as: 'div'
65
- }
66
-
67
- // `this.state` is already reserved for `virtual-scroller`.
68
- // static getDerivedStateFromProps(props, state) {
69
- // return {
70
- // prevProps: {
71
- // items: props.items
72
- // }
73
- // }
74
- // }
75
-
76
- container = React.createRef()
77
-
78
- // Handler function caches.
79
- // Just so that the props passed to `itemComponent`
80
- // are not changed on every `.render()` and so
81
- // `itemComponent` won't re-render if it's a `PureComponent`.
82
- onItemStateChange = new Array(this.props.items.length)
83
- onItemHeightChange = new Array(this.props.items.length)
84
-
85
- // Item refs for `.renderItem(i)`.
86
- itemRefs = new Array(this.props.items.length)
87
-
88
- // List items are rendered with `key`s
89
- // so that React doesn't reuse `itemComponent`s
90
- // in cases when `items` are changed.
91
- itemKeyPrefixes = []
92
-
93
- constructor(props) {
94
- super(props)
95
- // `this.previousItemsProperty` is only used for comparing
96
- // `previousItems` with `newItems` inside `render()`.
97
- this.previousItemsProperty = props.items
98
- // Generate unique `key` prefix for list item components.
99
- this.generateItemKeyPrefix()
100
- // Create `VirtualScroller` instance.
101
- this.createVirtualScroller()
102
- }
103
-
104
- createVirtualScroller() {
105
- const {
106
- as: AsComponent,
107
- items,
108
- initialState,
109
- initialCustomState,
110
- onStateChange,
111
- estimatedItemHeight,
112
- preserveScrollPositionOfTheBottomOfTheListOnMount,
113
- // `preserveScrollPositionAtBottomOnMount` property name is deprecated,
114
- // use `preserveScrollPositionOfTheBottomOfTheListOnMount` property instead.
115
- preserveScrollPositionAtBottomOnMount,
116
- initialScrollPosition,
117
- onScrollPositionChange,
118
- measureItemsBatchSize,
119
- scrollableContainer,
120
- // `getScrollableContainer` property is deprecated.
121
- // Use `scrollableContainer` instead.
122
- getScrollableContainer,
123
- getColumnsCount,
124
- getItemId,
125
- bypass,
126
- // bypassBatchSize
127
- } = this.props
128
- // Create `virtual-scroller` instance.
129
- this.virtualScroller = new VirtualScrollerCore(
130
- () => this.container.current,
131
- items,
132
- {
133
- _useTimeoutInRenderLoop: true,
134
- estimatedItemHeight,
135
- bypass,
136
- // bypassBatchSize,
137
- onItemInitialRender: this.onItemInitialRender,
138
- // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
139
- onItemFirstRender: this.onItemFirstRender,
140
- preserveScrollPositionOfTheBottomOfTheListOnMount,
141
- // `preserveScrollPositionAtBottomOnMount` property name is deprecated,
142
- // use `preserveScrollPositionOfTheBottomOfTheListOnMount` property instead.
143
- preserveScrollPositionAtBottomOnMount,
144
- initialScrollPosition,
145
- onScrollPositionChange,
146
- shouldUpdateLayoutOnScreenResize: this.shouldUpdateLayoutOnScreenResize,
147
- measureItemsBatchSize,
148
- scrollableContainer,
149
- // `getScrollableContainer` property is deprecated.
150
- // Use `scrollableContainer` instead.
151
- getScrollableContainer,
152
- getColumnsCount,
153
- getItemId,
154
- tbody: AsComponent === 'tbody',
155
- state: initialState,
156
- customState: initialCustomState,
157
- onStateChange,
158
- getState: () => this.state,
159
- setState: (newState, { willUpdateState, didUpdateState }) => {
160
- this.willUpdateState = willUpdateState
161
- this.didUpdateState = didUpdateState
162
- if (this.state) {
163
- // Update existing state.
164
- //
165
- // In case of hypothetically rewriting this in React hooks,
166
- // it wouldn't simply be `setState({ ...prevState, ...newState })`.
167
- // The reason is that `setState()` would be "asynchronous" (not immediate),
168
- // and `...prevState` would be stale in cases when more than a single
169
- // `setState()` call is made before the state actually updates,
170
- // making `prevState` stale, and, as a consequence, losing some
171
- // of the state updates.
172
- // For example, the first `setState()` call updates shown item indexes,
173
- // and the second `setState()` call updates `verticalSpacing`:
174
- // if it was simply `setState({ ...prevState, ...newState })`,
175
- // then the second state update could overwrite the first state update,
176
- // resulting in incorrect items being shown/hidden.
177
- //
178
- // I guess, in hooks, it could be something like:
179
- //
180
- // const [firstShownItemIndex, setFirstShownItemIndex] = useState()
181
- // ...
182
- // const setState = useCallback((newState) => {
183
- // for (const key in newState) {
184
- // switch (key) {
185
- // case 'firstShownItemIndex':
186
- // setFirstShownItemIndex(newState[key])
187
- // break
188
- // ...
189
- // }
190
- // }
191
- // setFirstShownItemIndex
192
- // }, [])
193
- // const virtualScroller = new VirtualScrollerCore({
194
- // setState,
195
- // ...
196
- // })
197
- // // `getState()` function would be updated on every render.
198
- // virtualScroller.getState = () => ({
199
- // firstShownItemIndex,
200
- // ...
201
- // })
202
- //
203
- // But as long as it uses the classic `this.setState()`,
204
- // it's fine and simple.
205
- this.setState(newState)
206
- } else {
207
- // Set initial state.
208
- willUpdateState(newState)
209
- this.state = newState
210
- didUpdateState()
211
- }
212
- }
213
- }
214
- )
215
- }
216
-
217
- // This is a proxy for `VirtualScroller`'s `.updateLayout` instance method.
218
- updateLayout = () => this.virtualScroller.updateLayout()
219
-
220
- // `.layout()` method name is deprecated, use `.updateLayout()` instead.
221
- layout = () => this.updateLayout()
222
-
223
- // This proxy is required for cases when
224
- // `onItemInitialRender` property changes at subsequent renders.
225
- // For example, if it's passed as an "anonymous" function:
226
- // `<VirtualScroller onItemInitialRender={() => ...}/>`.
227
- // In such cases, if this "proxy" workaround hasn't been implemented,
228
- // the `VirtualScroller` instance would have the reference to the old function.
229
- onItemInitialRender = (...args) => {
230
- const { onItemInitialRender } = this.props
231
- if (onItemInitialRender) {
232
- onItemInitialRender(...args)
233
- }
234
- }
235
-
236
- // This proxy is required for cases when
237
- // `onItemFirstRender` property changes at subsequent renders.
238
- // For example, if it's passed as an "anonymous" function:
239
- // `<VirtualScroller onItemFirstRender={() => ...}/>`.
240
- // In such cases, if this "proxy" workaround hasn't been implemented,
241
- // the `VirtualScroller` instance would have the reference to the old function.
242
- // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
243
- onItemFirstRender = (...args) => {
244
- const { onItemFirstRender } = this.props
245
- if (onItemFirstRender) {
246
- onItemFirstRender(...args)
247
- }
248
- }
249
-
250
- shouldUseRefs() {
251
- // There's no way to detect if `ref` can be passed to `component`:
252
- // https://github.com/facebook/react/issues/16309
253
- // So it only uses `ref`s for `React.Component`s.
254
- const { itemComponent } = this.props
255
- return isComponentClass(itemComponent)
256
- }
257
-
258
- /**
259
- * Returns a `key` for an `item`'s element.
260
- * @param {object} item — The item.
261
- * @param {number} i — Item's index in `items` list.
262
- * @return {any}
263
- */
264
- getItemKey(item, i) {
265
- const { getItemId } = this.props
266
- if (getItemId) {
267
- return getItemId(item)
268
- }
269
- return `${this.itemKeyPrefix}:${i}`
270
- }
271
-
272
- /**
273
- * A proxy to `VirtualScroller.getItemCoordinates(i)`.
274
- * @param {number} i
275
- * @return {object}
276
- */
277
- /*
278
- getItemCoordinates(i) {
279
- return this.virtualScroller.getItemCoordinates(i)
280
- }
281
- */
282
-
283
- /**
284
- * `updateItem(i)` has been renamed to `renderItem(i)`.
285
- * @param {number} i
286
- */
287
- updateItem(i) {
288
- return this.renderItem(i)
289
- }
290
-
291
- /**
292
- * Re-renders an item.
293
- * @param {number} i
294
- */
295
- renderItem(i) {
296
- i = this.getItemIndex(i)
297
- if (i === undefined) {
298
- return reportError(`Item ${JSON.stringify(i)} not found when calling ".renderItem()"`)
299
- }
300
- if (this.shouldUseRefs()) {
301
- // The item may be non-rendered when `.renderItem(i)` is called on it.
302
- // For example, when there's a "parent comment" having several "replies"
303
- // each of which has an autogenerated quote of the "parent comment"
304
- // and then the "parent comment" is updated (for example, a YouTube video
305
- // link gets parsed into an embedded video player) and all of its "replies"
306
- // should be updated too to show the parsed video title instead of the URL,
307
- // so `.renderItem(i)` is simply called on all of the "parent post"'s replies
308
- // regardless of some of those replies being rendered or not.
309
- if (this.itemRefs[i] && this.itemRefs[i].current) {
310
- const { items } = this.props
311
- // Stores `item` here because the `i` index
312
- // might have changed when the callback is called,
313
- // or the item even may have been removed.
314
- const item = items[i]
315
- this.itemRefs[i].current.forceUpdate(() => {
316
- if (this._isMounted) {
317
- // Recalculates the `i` index here because it
318
- // might have changed when the callback is called,
319
- // or the item even may have been removed.
320
- const i = items.indexOf(item)
321
- if (i >= 0) {
322
- this.virtualScroller.onItemHeightChange(i)
323
- }
324
- }
325
- })
326
- }
327
- }
328
- }
329
-
330
- getItemIndex(i) {
331
- if (typeof i === 'number') {
332
- return i
333
- }
334
- if (typeof i === 'object' && i !== null) {
335
- const { items, getItemId } = this.props
336
- const item = i
337
- i = 0
338
- while (i < items.length) {
339
- if (getItemId) {
340
- if (getItemId(item) === getItemId(items[i])) {
341
- return i
342
- }
343
- } else {
344
- if (item === items[i]) {
345
- return i
346
- }
347
- }
348
- i++
349
- }
350
- }
351
- }
352
-
353
- // Functional components can't have a `ref` assigned to them.
354
- // Item `ref`s are only used for calling `.renderItem(i)` instance method,
355
- // because `.renderItem(i)` calls `.forceUpdate()` on item `i`.
356
- // If a developer is not using the `.renderItem(i)` instance method
357
- // then `ref`s aren't required and will be omitted.
358
- getItemRef(i) {
359
- if (!this.itemRefs[i]) {
360
- this.itemRefs[i] = React.createRef()
361
- }
362
- return this.itemRefs[i]
363
- }
364
-
365
- getOnItemStateChange(i) {
366
- if (!this.onItemStateChange[i]) {
367
- this.onItemStateChange[i] = (itemState) => this.virtualScroller.onItemStateChange(i, itemState)
368
- }
369
- return this.onItemStateChange[i]
370
- }
371
-
372
- getOnItemHeightChange(i) {
373
- if (!this.onItemHeightChange[i]) {
374
- this.onItemHeightChange[i] = () => this.virtualScroller.onItemHeightChange(i)
375
- }
376
- return this.onItemHeightChange[i]
377
- }
378
-
379
- generateItemKeyPrefix() {
380
- const prefix = String(Math.random()).slice(2)
381
- if (this.itemKeyPrefixes.indexOf(prefix) >= 0) {
382
- return this.generateItemKeyPrefix()
383
- }
384
- this.itemKeyPrefixes.push(prefix)
385
- this.itemKeyPrefix = prefix
386
- }
387
-
388
- componentDidMount() {
389
- const { onMount } = this.props
95
+ }, [])
96
+
97
+ // Start `VirtualScroller` on mount.
98
+ // Stop `VirtualScroller` on unmount.
99
+ useVirtualScrollerStartStop(virtualScroller)
100
+
101
+ // List items are rendered with `key`s so that React doesn't
102
+ // "reuse" `itemComponent`s in cases when `items` are changed.
103
+ const {
104
+ getItemKey,
105
+ updateItemKeysForNewItems
106
+ } = useItemKeys({
107
+ getItemId
108
+ })
109
+
110
+ // Cache per-item `onItemStateChange` functions' "references"
111
+ // so that item components don't get re-rendered needlessly.
112
+ const getOnItemStateChange = useOnItemStateChange({
113
+ items,
114
+ virtualScroller
115
+ })
116
+
117
+ // Cache per-item `onItemHeightChange` functions' "references"
118
+ // so that item components don't get re-rendered needlessly.
119
+ const getOnItemHeightChange = useOnItemHeightChange({
120
+ items,
121
+ virtualScroller
122
+ })
123
+
124
+ // Detect if `items` have changed.
125
+ useHandleItemsChange(items, {
126
+ virtualScroller,
127
+ // `preserveScrollPosition` property name is deprecated,
128
+ // use `preserveScrollPositionOnPrependItems` property instead.
129
+ preserveScrollPosition,
130
+ preserveScrollPositionOnPrependItems,
131
+ updateItemKeysForNewItems
132
+ })
133
+
134
+ // Add instance methods to the React component.
135
+ useInstanceMethods(ref, {
136
+ virtualScroller
137
+ })
138
+
139
+ useLayoutEffect(() => {
140
+ // (deprecated)
390
141
  // `onMount()` option is deprecated due to no longer being used.
391
142
  // If someone thinks there's a valid use case for it, create an issue.
392
143
  if (onMount) {
393
144
  onMount()
394
145
  }
395
- this._isMounted = true
396
- // Start listening to scroll events.
397
- this.virtualScroller.listen()
398
- }
146
+ }, [])
147
+
148
+ // `willRender()` function is no longer used.
149
+ //
150
+ // // `getSnapshotBeforeUpdate()` is called right before `componentDidUpdate()`.
151
+ // // A hook equivalent/workaround for `getSnapshotBeforeUpdate()`:
152
+ // // https://github.com/facebook/react/issues/15221#issuecomment-583448887
153
+ // //
154
+ // getSnapshotBeforeUpdate(prevProps, prevState) {
155
+ // if (this.state !== prevState) {
156
+ // this.willRender(this.state, prevState)
157
+ // }
158
+ // // Returns `null` to avoid React warning:
159
+ // // "A snapshot value (or null) must be returned. You have returned undefined".
160
+ // return null
161
+ // }
399
162
 
400
- // `getSnapshotBeforeUpdate()` is called right before `componentDidUpdate()`.
401
- getSnapshotBeforeUpdate(prevProps, prevState) {
402
- if (this.state !== prevState) {
403
- this.willUpdateState(this.state, prevState)
404
- }
405
- // Returns `null` to avoid React warning:
406
- // "A snapshot value (or null) must be returned. You have returned undefined".
407
- return null
408
- }
163
+ className = useClassName(className, {
164
+ tbody
165
+ })
166
+
167
+ const style = useStyle({
168
+ tbody,
169
+ virtualScroller
170
+ })
171
+
172
+ const {
173
+ items: renderedItems,
174
+ itemStates,
175
+ firstShownItemIndex,
176
+ lastShownItemIndex
177
+ } = virtualScroller.getState()
178
+
179
+ return (
180
+ <AsComponent
181
+ {...rest}
182
+ ref={container}
183
+ className={className}
184
+ style={style}>
185
+ {renderedItems.map((item, i) => {
186
+ if (i >= firstShownItemIndex && i <= lastShownItemIndex) {
187
+ return (
188
+ <Component
189
+ {...itemComponentProps}
190
+ key={getItemKey(item, i)}
191
+ state={itemStates && itemStates[i]}
192
+ onStateChange={getOnItemStateChange(i)}
193
+ onHeightChange={getOnItemHeightChange(i)}>
194
+ {item}
195
+ </Component>
196
+ )
197
+ }
198
+ return null
199
+ })}
200
+ </AsComponent>
201
+ )
202
+ }
409
203
 
410
- // `componentDidUpdate()` is called immediately after React component has re-rendered.
411
- // That would correspond to `useLayoutEffect()` in React Hooks.
412
- componentDidUpdate(prevProps, prevState) {
413
- // If `state` did change.
414
- if (this.state !== prevState) {
415
- this.didUpdateState(prevState)
416
- }
417
- // If `items` property did change then update `virtual-scroller` items.
418
- // This could have been done in `.render()` but `.setItems()` calls
419
- // `.setState()` internally which would result in React throwing an error.
420
- const {
421
- items,
422
- preserveScrollPosition,
423
- preserveScrollPositionOnPrependItems
424
- } = this.props
425
- if (items !== prevProps.items) {
426
- this.virtualScroller.setItems(items, {
427
- // `preserveScrollPosition` property name is deprecated,
428
- // use `preserveScrollPositionOnPrependItems` instead.
429
- preserveScrollPositionOnPrependItems: preserveScrollPositionOnPrependItems || preserveScrollPosition
430
- })
431
- }
432
- }
204
+ VirtualScroller = React.forwardRef(VirtualScroller)
433
205
 
434
- componentWillUnmount() {
435
- this._isMounted = false
436
- // Stop listening to scroll events.
437
- this.virtualScroller.stop()
438
- }
206
+ export default VirtualScroller
439
207
 
440
- render() {
441
- const {
442
- as: AsComponent,
443
- itemComponent: Component,
444
- itemComponentProps,
445
- // Rest
446
- items: _items,
447
- estimatedItemHeight,
448
- bypass,
449
- // bypassBatchSize,
450
- preserveScrollPositionOnPrependItems,
451
- // `preserveScrollPosition` property name is deprecated,
452
- // use `preserveScrollPositionOnPrependItems` instead.
453
- preserveScrollPosition,
454
- preserveScrollPositionOfTheBottomOfTheListOnMount,
455
- // `preserveScrollPositionAtBottomOnMount` property name is deprecated,
456
- // use `preserveScrollPositionOfTheBottomOfTheListOnMount` property instead.
457
- preserveScrollPositionAtBottomOnMount,
458
- initialScrollPosition,
459
- onScrollPositionChange,
460
- measureItemsBatchSize,
461
- scrollableContainer,
462
- // `getScrollableContainer` property is deprecated.
463
- // Use `scrollableContainer` instead.
464
- getScrollableContainer,
465
- getColumnsCount,
466
- initialState,
467
- initialCustomState,
468
- onStateChange,
469
- onItemInitialRender,
470
- // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
471
- onItemFirstRender,
472
- getItemId,
473
- onMount,
474
- className,
475
- ...rest
476
- } = this.props
477
- const {
478
- items,
479
- itemStates,
480
- firstShownItemIndex,
481
- lastShownItemIndex,
482
- beforeItemsHeight,
483
- afterItemsHeight
484
- } = this.virtualScroller.getState()
485
- // If `items` are about to be changed then
486
- // store the scroll Y position for the first one
487
- // of the current items.
488
- // Previously it was being done in `componentDidUpdate()`
489
- // but it was later found out that it wouldn't work
490
- // for "Show previous" button because it would
491
- // get hidden before `componentDidUpdate()` is called.
492
- //
493
- // Consider this code example:
494
- //
495
- // const { fromIndex, items } = this.state
496
- // const items = allItems.slice(fromIndex)
497
- // return (
498
- // {fromIndex > 0 &&
499
- // <button onClick={this.onShowPrevious}>
500
- // Show previous
501
- // </button>
502
- // }
503
- // <VirtualScroller
504
- // items={items}
505
- // itemComponent={ItemComponent}/>
506
- // )
507
- //
508
- // Consider a user clicks "Show previous" to show the items from the start.
509
- // By the time `componentDidUpdate()` is called on `<VirtualScroller/>`,
510
- // the "Show previous" button has already been hidden
511
- // (because there're no more "previous" items)
512
- // which results in the scroll Y position jumping forward
513
- // by the height of that "Show previous" button.
514
- // This is because `<VirtualScroller/>` captures scroll Y
515
- // position when items are prepended via `.setItems()`
516
- // when the "Show previous" button is still being shown,
517
- // and then restores scroll Y position in `.didUpdateState()`
518
- // when the "Show previous" button has already been hidden:
519
- // that's the reason for the scroll Y "jump".
520
- //
521
- // To prevent that, scroll Y position is captured at `render()`
522
- // time rather than later in `componentDidUpdate()`: this way,
523
- // scroll Y position is captured while the "Show previous" button
524
- // is still being shown.
525
- //
526
- const newItems = this.props.items
527
- const previousItems = items // this.virtualScroller.getState().items
528
- // There's one case when `newItems !== previousItems` is `true`
529
- // from the start: when `initialState.items` are passed.
530
- // To handle that single case `this.previousItemsProperty`
531
- // is tracked and `this.itemsPropertyHasChanged` flag is set.
532
- if (!this.itemsPropertyWasChanged) {
533
- this.itemsPropertyWasChanged = this.props.items !== this.previousItemsProperty
534
- }
535
- this.previousItemsProperty = this.props.items
536
- if (this.itemsPropertyWasChanged && newItems !== previousItems) {
537
- const itemsDiff = this.virtualScroller.getItemsDiff(previousItems, newItems)
538
- if (itemsDiff && itemsDiff.prependedItemsCount === 0 && itemsDiff.appendedItemsCount > 0) {
539
- // If it's just items that have been appended
540
- // then no need to re-generate the prefix
541
- // and to fix scroll position and to clear caches.
542
- } else {
543
- // If the items update was incremental, then it's possible
544
- // that some items were prepended, and so the scroll Y position
545
- // should be restored after rendering those new items
546
- // in order for the currently shown items to stay
547
- // on the same position on screen.
548
- // (only if explicitly opted into using this feature)
549
- //
550
- // If the items update wasn't incremental
551
- // then there's no point in restoring scroll position.
552
- //
553
- // `preserveScrollPosition` property name is deprecated,
554
- // use `preserveScrollPositionOnPrependItems` instead.
555
- //
556
- if (itemsDiff) {
557
- const { prependedItemsCount } = itemsDiff
558
- if (prependedItemsCount > 0) {
559
- if (preserveScrollPositionOnPrependItems || preserveScrollPosition) {
560
- if (firstShownItemIndex === 0) {
561
- this.virtualScroller.restoreScroll.captureScroll({
562
- previousItems,
563
- newItems,
564
- prependedItemsCount
565
- })
566
- }
567
- }
568
- }
569
- }
570
- // Reset the unique `key` prefix for item component keys.
571
- if (!getItemId) {
572
- this.generateItemKeyPrefix()
573
- }
574
- // Reset item refs.
575
- this.itemRefs = new Array(newItems.length)
576
- }
577
- }
578
- const tbody = this.virtualScroller.tbody
579
- return (
580
- <AsComponent
581
- {...rest}
582
- ref={this.container}
583
- className={tbody ? (className ? className + ' ' + 'VirtualScroller' : 'VirtualScroller') : className}
584
- style={{
585
- paddingTop: tbody ? undefined : px(beforeItemsHeight),
586
- paddingBottom: tbody ? undefined : px(afterItemsHeight)
587
- }}>
588
- {items.map((item, i) => {
589
- if (i >= firstShownItemIndex && i <= lastShownItemIndex) {
590
- return (
591
- <Component
592
- {...itemComponentProps}
593
- key={this.getItemKey(item, i)}
594
- ref={this.shouldUseRefs() ? this.getItemRef(i) : undefined}
595
- state={itemStates && itemStates[i]}
596
- onStateChange={this.getOnItemStateChange(i)}
597
- onHeightChange={this.getOnItemHeightChange(i)}>
598
- {item}
599
- </Component>
600
- )
601
- }
602
- return null
603
- })}
604
- </AsComponent>
605
- )
606
- }
208
+ // `PropTypes.elementType` is available in some version of `prop-types`.
209
+ // https://github.com/facebook/prop-types/issues/200
210
+ const elementType = PropTypes.elementType || PropTypes.oneOfType([
211
+ PropTypes.string,
212
+ PropTypes.func,
213
+ PropTypes.object
214
+ ])
215
+
216
+ VirtualScroller.propTypes = {
217
+ as: elementType,
218
+ items: PropTypes.arrayOf(PropTypes.any).isRequired,
219
+ itemComponent: elementType.isRequired,
220
+ itemComponentProps: PropTypes.object,
221
+ estimatedItemHeight: PropTypes.number,
222
+ bypass: PropTypes.bool,
223
+ // bypassBatchSize: PropTypes.number,
224
+ tbody: PropTypes.bool,
225
+ preserveScrollPositionOnPrependItems: PropTypes.bool,
226
+ // `preserveScrollPosition` property name is deprecated,
227
+ // use `preserveScrollPositionOnPrependItems` instead.
228
+ preserveScrollPosition: PropTypes.bool,
229
+ measureItemsBatchSize: PropTypes.number,
230
+ // `scrollableContainer` property is deprecated.
231
+ // Use `getScrollableContainer()` property instead.
232
+ scrollableContainer: PropTypes.any,
233
+ getScrollableContainer: PropTypes.func,
234
+ getColumnsCount: PropTypes.func,
235
+ getItemId: PropTypes.func,
236
+ className: PropTypes.string,
237
+ onMount: PropTypes.func,
238
+ onItemInitialRender: PropTypes.func,
239
+ // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
240
+ onItemFirstRender: PropTypes.func,
241
+ initialScrollPosition: PropTypes.number,
242
+ onScrollPositionChange: PropTypes.func,
243
+ onStateChange: PropTypes.func,
244
+ initialState: PropTypes.shape({
245
+ items: PropTypes.arrayOf(PropTypes.object).isRequired,
246
+ itemStates: PropTypes.arrayOf(PropTypes.any),
247
+ firstShownItemIndex: PropTypes.number.isRequired,
248
+ lastShownItemIndex: PropTypes.number.isRequired,
249
+ beforeItemsHeight: PropTypes.number.isRequired,
250
+ afterItemsHeight: PropTypes.number.isRequired,
251
+ itemHeights: PropTypes.arrayOf(PropTypes.number).isRequired,
252
+ columnsCount: PropTypes.number,
253
+ verticalSpacing: PropTypes.number
254
+ })
607
255
  }
608
256
 
609
- /**
610
- * Checks if the argument is a `React.Component` class.
611
- * https://overreacted.io/how-does-react-tell-a-class-from-a-function/
612
- * @param {any} Component
613
- * @return {object} [result] Returns `undefined` if it's not a `React.Component`. Returns an empty object if it's a `React.Component` (`.isReactComponent` is an empty object).
614
- */
615
- function isComponentClass(Component) {
616
- // return Component.prototype instanceof React.Component
617
- // `React.memo()` returns `.prototype === undefined` for some reason.
618
- return Component.prototype && Component.prototype.isReactComponent
257
+ VirtualScroller.defaultProps = {
258
+ as: 'div'
619
259
  }