'MUI DataGrid in Safari is sometimes not scrollable vertically

Using MUI core's <Box> and MUIX's <DataGrid> components (and some other ones) I've outlined a data grid that looks like this:

enter image description here

Naturally, when the number of rows is bigger that what fits in the grid body, a vertical scrollbar appears. That is, it appears in Google Chrome (Beta, 102.0.5005.61), but in Safari (15.4 (17613.1.17.1.13)) it does not. The video shows how I am trying to scroll it, but it behaves like an unscrollable (no-overflow) list, even though the grid body clearly doesn't fit all rows, and there is an overflow. Only when I change the viewport height (e.g., by opening dev tools bottom panel) does it briefly become scrollable, – until I refresh the data or the browser page, and it gets back to being unscrollable.

Is there a way I can fix this? It looks like I am doing something slightly wrong, and Safari is being too strict about it, but my buddy Chrome correcting the mistake.


I understand that the devil is in the details, and I might miss some. Annoying as it is, feel free to ask whatever you need. Other than that, there's two things that might be important:

  • List sorting and slicing is server-side. Initially the component renders "nil" data: empty columns and rows, null event listeners, empty string instead of the ID property key, etc. Upon loading, the component fetches data and after that almost immediately renders again the second time – now with a non-nil data.
  • The outline of the app components (omitting presumably irrelevant ones) is this:
const flexParent = { display: 'flex', flexDirection: 'column' };
const flexChild = { flexGrow: 1 };
<Box sx={{ height: '100vh', ...flexParent }}>
  <Container sx={{ ...flexChild, ...flexParent }}>
    <Box sx={{ maxHeight: '100%', ...flexChild, ...flexParent }}>
      <Box sx={{ ...flexChild, ...flexParent }}>
        <Box sx={{ ...flexChild }}>
          <Box sx={{ height: '100%', width: '100%' }}>
            <DataGrid
              loading={loading}

              // data: view
              columns={columns}
              rows={rows}

              // pagination: sorting
              sortingMode='server'
              sortModel={sortModel}
              onSortModelChange={changeOrder}

              // pagination: slicing
              paginationMode='server'
              rowCount={totalCount}
              rowsPerPageOptions={limitOptions}
              pageSize={limit}
              page={pageIndex}
              onPageChange={changePageIndex}
              onPageSizeChange={changeLimit}

              // styles
              disableColumnSelector
              disableDensitySelector
              hideFooterSelectedRowCount
              showCellRightBorder
              sx={{
                '.MuiDataGrid-cell, .MuiDataGrid-columnHeader': {
                  fontFamily: 'monospace',
                },
                '.MuiDataGrid-columnHeader:last-child .MuiDataGrid-columnSeparator': {
                  display: 'none',
                },
              }}
            />
          </Box>
        </Box>
      </Box>
    </Box>
  </Container>
</Box>


Solution 1:[1]

I've found a solution workaround, but not everyone's gonna like it.

The reason for the problem (as I thought) was the first "thing that might be important":

Initially the component renders "nil" data: empty columns and rows, null event listeners, empty string instead of the ID property key, etc. Upon loading, the component fetches data and after that almost immediately renders again the second time – now with a non-nil data.

Looks like because of that some race condition arises internally, that leads to the component thinking that there are no rows to scroll over, while in reality there are plenty of rows to scroll over.

Adding key={Math.random()} to the grid component solves the problem by forcing it to re-render at every change:

<DataGrid
  key={Math.random()}
  columns={columns}
  rows={rows}
/>

This now means that the previous "nil" state is completely discarded, removing any and all possible effects on the new "non-nil" state; this ensures consistent state of the component.

Obviously, this is costly. But for now this works for me (there's not much to re-render anyway), and might work for you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1