useVirtualizedGrid.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import {
  2. DependencyList,
  3. RefObject,
  4. useCallback,
  5. useEffect,
  6. useMemo,
  7. useState,
  8. } from 'react';
  9. import {CellMeasurerCache, CellMeasurerCacheParams, MultiGrid} from 'react-virtualized';
  10. type Opts = {
  11. /**
  12. * Options for the CellMeasurerCache constructor
  13. */
  14. cellMeasurer: CellMeasurerCacheParams;
  15. /**
  16. * How many columns are being rendered
  17. */
  18. columnCount: number;
  19. /**
  20. * List of other values that should trigger re-computing column sizes
  21. */
  22. deps: DependencyList;
  23. /**
  24. * There must be one column with a dynamic width, so the table can fill all available width inside the container
  25. */
  26. dynamicColumnIndex: number;
  27. /**
  28. * The <MultiGrid> elem.
  29. */
  30. gridRef: RefObject<MultiGrid>;
  31. };
  32. const globalCellMeasurerCache = new WeakMap<CellMeasurerCacheParams, CellMeasurerCache>();
  33. function useVirtualizedGrid({
  34. cellMeasurer,
  35. columnCount,
  36. deps,
  37. dynamicColumnIndex,
  38. gridRef,
  39. }: Opts) {
  40. const cache = useMemo(() => {
  41. if (globalCellMeasurerCache.has(cellMeasurer)) {
  42. return globalCellMeasurerCache.get(cellMeasurer)!;
  43. }
  44. const newCellMeasurer = new CellMeasurerCache(cellMeasurer);
  45. globalCellMeasurerCache.set(cellMeasurer, newCellMeasurer);
  46. return newCellMeasurer;
  47. }, [cellMeasurer]);
  48. const [scrollBarWidth, setScrollBarWidth] = useState(0);
  49. const onWrapperResize = useCallback(() => {
  50. // TODO: debounce?
  51. gridRef.current?.recomputeGridSize({columnIndex: dynamicColumnIndex});
  52. }, [gridRef, dynamicColumnIndex]);
  53. // Recompute the width of the dynamic column when deps change (ie: a search/filter is applied)
  54. useEffect(onWrapperResize, [onWrapperResize, deps]);
  55. const onScrollbarPresenceChange = useCallback(({vertical, size}) => {
  56. setScrollBarWidth(vertical ? size : 0);
  57. }, []);
  58. const getColumnWidth = useCallback(
  59. (width: number) =>
  60. ({index}) => {
  61. if (index !== dynamicColumnIndex) {
  62. return cache.columnWidth({index});
  63. }
  64. const columns = Array.from(new Array(columnCount));
  65. const fullWidth = width - scrollBarWidth;
  66. // Take the full width available, and remove all the static/cached widths
  67. // so we know how much space is available for our dynamic column.
  68. const colWidth = columns.reduce(
  69. (remainingWidth, _, i) =>
  70. i === dynamicColumnIndex
  71. ? remainingWidth
  72. : remainingWidth - cache.columnWidth({index: i}),
  73. fullWidth
  74. );
  75. return Math.max(colWidth, 200);
  76. },
  77. [cache, columnCount, dynamicColumnIndex, scrollBarWidth]
  78. );
  79. return {
  80. cache,
  81. getColumnWidth,
  82. onScrollbarPresenceChange,
  83. onWrapperResize,
  84. };
  85. }
  86. export default useVirtualizedGrid;