[data grid] Fix noRowsOverlay flicker between dataSource re-fetches#22465
[data grid] Fix noRowsOverlay flicker between dataSource re-fetches#22465LukasTy wants to merge 2 commits into
noRowsOverlay flicker between dataSource re-fetches#22465Conversation
The navigation event handlers cleared rows synchronously before queueing the fetch. Because `setRows` rebuilds `state.rows` from `props.loading`, the synchronous clear also reset `loading` to false, exposing a render with `rows = []` and `loading = false` that triggered `noRowsOverlay`. The flash was most visible when navigating between already-cached pages. Now the handlers call `setLoading(true)` after `setRows([])`, so the loading state survives the `setRows` rebuild and the overlay selector picks `loadingOverlay` instead. Add the `dataSourceKeepPreviousData` prop to opt into keeping the previously displayed rows visible during the fetch (mirrors React Query's `placeholderData: keepPreviousData`). Fixes mui#22458 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploy previewBundle size
Check out the code infra dashboard for more information about this PR. |
noRowsOverlay flicker between dataSource re-fetchesnoRowsOverlay flicker between dataSource re-fetches
…ousData` The error path in `handleDataUpdate` and `handleGroupedDataUpdate` cleared rows unconditionally, so a rejected refetch wiped the last known-good data even when `dataSourceKeepPreviousData` was on — the opposite of what users opt into the prop for. Gate both `setRows([])` calls behind the prop so a failed refetch leaves the previous rows visible while `onDataSourceError` delivers the error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hey Lukas, Im the original reporter of this issue. I figured I'll share my thinking here instead of my own PR since this one is more comprehensive anyway. Im personally happy with this approach and would love to have any resolution honestly. Just two things to consider:
<DataGrid
dataSource={{
fetchRows: ...,
keepPreviousData: true
}}The second is more subjective, but for the first I'll offer an alternative. You can keep the previous behavior (don't persist previous data) and get rid of the flash by removing the "debounce" on the My ideal solution would be to do both. Remove debounce (if acceptable) to improve default behavior for everyone out of the box and add this new prop for people like me who prefer keeping previous rows mounted for aesthetic purposes. |
|
@GMchris thank you for the response. In regards to the |
Fixes #22458
Closes #22460.
Summary
dataSourceAPI. The flash is especially visible on cached navigation, where the response is available synchronously but a stale empty render still appears for a frame. Reported in Previous cache entry not persisted duringdataSource.fetchRowscalls resulting in no rows flicker #22458.dataSourceKeepPreviousDataprop that keeps the previously displayed rows visible during the fetch (mirrors TanStack Query'splaceholderData: keepPreviousDataand SWR'skeepPreviousData).Root cause
The navigation event handlers (
handleFetchRowsOnParamsChangeinuseGridDataSourceBaseandhandleRowGroupingModelChangeinuseGridDataSourcePremium) calledapiRef.current.setRows([])synchronously and queueddebouncedFetchRows()afterwards. InsideuseGridRows,setRowsrebuildsstate.rowsfromprops.loadingviagetRowsStateFromCache. BecausedataSourcegrids don't pass aloadingprop, that rebuild resetstate.rows.loadingtoundefined, exposing a render withrows = []andloading = false. The overlay selector then pickednoRowsOverlayfor that single frame.Fix
In both navigation handlers, call
setLoading(true)immediately aftersetRows([])so the loading flag survives thesetRowsrebuild. The first render after the click now hasrows = []andloading = true, which picksloadingOverlay(skeleton variant) instead ofnoRowsOverlay. The cache-hit branch offetchRowsnow also callssetLoading(false)so the loading flag the handler just set is cleared when the cached response is applied synchronously.The new
dataSourceKeepPreviousDataprop guards thesetRows([])call so the previous rows stay visible during the fetch and the loading overlay (linear-progress variant, since rows are non-empty) is shown on top.Test plan
dataSource.DataGrid.test.tsxcovering cache-hit, cache-miss, empty-response, and the newdataSourceKeepPreviousDataopt-in.pnpm test:unit --project "x-data-grid*" --run— 1612 passed.pnpm typescriptclean across the monorepo.pnpm eslint,pnpm prettier,pnpm markdownlintclean.dataSourceKeepPreviousDataon the new demo keeps the previous rows visible with a linear-progress overlay on top.