DataGrid window bottom padding allows user to scroll data out of view (scrolls too far down)

Viewed 408

I'm working to get the DataGrix/XGrid data to trigger a request for more data when the user scrolls to the bottom of the MuiDataGrid-window. The triggering works. However, the issue is when does the triggering event occur.

What I mean by when: Right now it triggers when most of the data is no longer visible (i.e., when it hides above the window). I'm trying to figure out how to remove any "padding" at the bottom. I say "padding" in quotes, because I don't suspect it has anything to do with the css padding or margin attributes.

There are several html components at work: MuiDataGrid-window, -dataContainer, -renderingZone. There is also a Mui-resizeTriggers and expand-trigger.

I've looked, compared and contrasted what works in the Mui docs vs what I have up and running.

I suspect that in general, there is a way to align the scroll bar with the bottom of the page. The top seems to always know where the top is :))

Has anyone any experience formatting the layout to adjust where the bottom registers/references the bottom of the scroll bar?

Thank you to anyone in advance.

1 Answers

Summary answer

The internal grid state would get out of sync with the rows prop. I'm sure there is a way to solve this otherwise, based on what the docs hint at what can be an issue, I went for the imperative use of the apiRef to update the rows and subsequently force an update.

Notes:

  1. There may be some redundant code

    • using the apiRef directly may not be required given I also created a new side-effect that tracks changes in api status && a "read-once" latch
    • calling both the apiRef to update the rows and then force an update is likely redundant; I suspect the forceUpdate is called internally following the call to update the rows
  2. the dynamic sizing is not required, but is an extra layer of functionality that highlights how the grid controls the number of rows in view (size of the "viewport" given a rowHeight setting).

What worked

There are two pieces to this: the first is setting-up how many rows to display. The second, is making sure the internal state of the grid is in fact being rendered. Prior to this solution, the extra scrolling was consistent with the "internal" state of the grid, not what was being rendered (the render was "behind" by a cycle).

The DataGrid size is set by wrapping the component in a component with a fixed size. In the docs they use a div. I compute the gridStyle.height using a derivative of MAX_ROWS and ROW_HEIGHT settings.

// max size is 9 rows before I start scrolling
const gridHeight = (maxRecords, rowHeight, header, footer, adjustment) => {
  const gridBody = Math.min(9, Math.max(2, maxRecords)) * rowHeight;
  return gridBody + header + footer + adjustment;
};

The grid wrapped in the div that uses the computed height:

<div style={gridStyle}>
    <DataGrid
     className={clsx('My-ValueGrid', type)}
     apiRef={apiRef}
     ...
    />
</div>

The scroll event handler sets the state readyForMore. This flag is the equivalent of a "latch" that ensures a single (not infinite) re-render when the requested data status === 'success'

const handleOnRowScrollEnd = ({ viewportPageSize }) => {
    if (apiRef.current.getVisibleRowModels().size < MAX_ROWS) {
      const nextPageSize = Math.max(PAGE_SIZE, viewportPageSize)
      fetchPage({ pageSize: nextPageSize, after: max });
      setReadyForMore(true);  // <<< open/reset the latch
    }
  };

The state flag readyForMore is then a trigger in the components main effect:

  useEffect(() => {
    if (status === 'pending') {
      /* do nothing */
    }
    if (status === 'error') {
      setError({ message: JSON.stringify(data.cache) });
    }
    if (status === 'success' && readyForMore) {
      const { rows: newRows, selectedRows: newSelected } = toGridValues(
        data.cache,
        storeSelectionModel,
      );

      setSelectionModel([...selectionModel, ...newSelected]);

      apiRef.current.updateRows(newRows); // <<< going imperative...
      apiRef.current.forceUpdate();  // <<< may not be required
      setReadyForMore(false); // <<< close the latch (prevent infinite renders)
      setMaxRows(data.cache.totalCount);  // <<< only changes when changing filters
    }
  }, [
    apiRef,
    data.cache,
    readyForMore,
    selectionModel,
    status,
    storeSelectionModel,
  ]);
Related