diff --git a/docs/data/data-grid/events/events.json b/docs/data/data-grid/events/events.json index e61873a789a6..2e7bd43681bf 100644 --- a/docs/data/data-grid/events/events.json +++ b/docs/data/data-grid/events/events.json @@ -249,7 +249,7 @@ "name": "rowsScroll", "description": "Fired during the scroll of the grid viewport.", "params": "GridScrollParams", - "event": "MuiEvent<{}>" + "event": "MuiEvent" }, { "name": "rowsScrollEnd", diff --git a/docs/src/pages/components/data-grid/events/events.json b/docs/src/pages/components/data-grid/events/events.json index e61873a789a6..2e7bd43681bf 100644 --- a/docs/src/pages/components/data-grid/events/events.json +++ b/docs/src/pages/components/data-grid/events/events.json @@ -249,7 +249,7 @@ "name": "rowsScroll", "description": "Fired during the scroll of the grid viewport.", "params": "GridScrollParams", - "event": "MuiEvent<{}>" + "event": "MuiEvent" }, { "name": "rowsScrollEnd", diff --git a/packages/grid/x-data-grid-generator/package.json b/packages/grid/x-data-grid-generator/package.json index cbb38467de2c..eccbe83516c7 100644 --- a/packages/grid/x-data-grid-generator/package.json +++ b/packages/grid/x-data-grid-generator/package.json @@ -45,7 +45,7 @@ "peerDependencies": { "@mui/icons-material": "^5.2.5", "@mui/material": "^5.2.8", - "react": "^17.0.2" + "react": "^17.0.2 || ^18.0.0" }, "setupFiles": [ "/src/setupTests.js" diff --git a/packages/grid/x-data-grid-pro/package.json b/packages/grid/x-data-grid-pro/package.json index 0a9ad0962893..731af17d40c2 100644 --- a/packages/grid/x-data-grid-pro/package.json +++ b/packages/grid/x-data-grid-pro/package.json @@ -53,7 +53,7 @@ "peerDependencies": { "@mui/material": "^5.2.8", "@mui/system": "^5.2.8", - "react": "^17.0.2" + "react": "^17.0.2 || ^18.0.0" }, "setupFiles": [ "/src/setupTests.js" diff --git a/packages/grid/x-data-grid-pro/src/components/DataGridProColumnHeaders.tsx b/packages/grid/x-data-grid-pro/src/components/DataGridProColumnHeaders.tsx index 23ef7d629d5d..42229dfbb151 100644 --- a/packages/grid/x-data-grid-pro/src/components/DataGridProColumnHeaders.tsx +++ b/packages/grid/x-data-grid-pro/src/components/DataGridProColumnHeaders.tsx @@ -122,27 +122,15 @@ export const DataGridProColumnHeaders = React.forwardRef< const pinnedColumns = useGridSelector(apiRef, gridPinnedColumnsSelector); const [leftPinnedColumns, rightPinnedColumns] = filterColumns(pinnedColumns, visibleColumnFields); - const { - isDragging, - renderContext, - updateInnerPosition, - getRootProps, - getInnerProps, - getColumns, - } = useGridColumnHeaders({ - innerRef, - minColumnIndex: leftPinnedColumns.length, - }); + const { isDragging, renderContext, getRootProps, getInnerProps, getColumns } = + useGridColumnHeaders({ + innerRef, + minColumnIndex: leftPinnedColumns.length, + }); const ownerState = { leftPinnedColumns, rightPinnedColumns, classes: rootProps.classes }; const classes = useUtilityClasses(ownerState); - React.useEffect(() => { - if (renderContext) { - updateInnerPosition(renderContext); - } - }, [renderContext, updateInnerPosition]); - const leftRenderContext = renderContext && leftPinnedColumns.length ? { diff --git a/packages/grid/x-data-grid-pro/src/components/DataGridProVirtualScroller.tsx b/packages/grid/x-data-grid-pro/src/components/DataGridProVirtualScroller.tsx index 454de43a2789..ad882cf95ad8 100644 --- a/packages/grid/x-data-grid-pro/src/components/DataGridProVirtualScroller.tsx +++ b/packages/grid/x-data-grid-pro/src/components/DataGridProVirtualScroller.tsx @@ -210,10 +210,6 @@ const DataGridProVirtualScroller = React.forwardRef< useGridApiEventHandler(apiRef, GridEvents.columnWidthChange, refreshRenderZonePosition); useGridApiEventHandler(apiRef, GridEvents.columnOrderChange, refreshRenderZonePosition); - React.useEffect(() => { - refreshRenderZonePosition(); - }, [refreshRenderZonePosition]); - const leftRenderContext = renderContext && leftPinnedColumns.length > 0 ? { diff --git a/packages/grid/x-data-grid/package.json b/packages/grid/x-data-grid/package.json index fd35b4d7d6fc..06379dee3270 100644 --- a/packages/grid/x-data-grid/package.json +++ b/packages/grid/x-data-grid/package.json @@ -54,7 +54,7 @@ "peerDependencies": { "@mui/material": "^5.2.8", "@mui/system": "^5.2.8", - "react": "^17.0.2" + "react": "^17.0.2 || ^18.0.0" }, "setupFiles": [ "/src/setupTests.js" diff --git a/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx b/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx index 975d56e31ce3..d6a05b1ef752 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import * as ReactDOM from 'react-dom'; import { useForkRef } from '@mui/material/utils'; import { useGridApiContext } from '../../utils/useGridApiContext'; import { useGridSelector } from '../../utils/useGridSelector'; @@ -26,6 +27,10 @@ interface UseGridColumnHeadersProps { minColumnIndex?: number; } +function isUIEvent(event: any): event is React.UIEvent { + return !!event.target; +} + export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const { innerRef: innerRefProp, minColumnIndex = 0 } = props; @@ -70,8 +75,14 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { [columnPositions, minColumnIndex, rootProps.columnBuffer], ); + React.useLayoutEffect(() => { + if (renderContext) { + updateInnerPosition(renderContext); + } + }, [renderContext, updateInnerPosition]); + const handleScroll = React.useCallback>( - ({ left, renderContext: nextRenderContext = null }) => { + ({ left, renderContext: nextRenderContext = null }, event) => { if (!innerRef.current) { return; } @@ -87,13 +98,30 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { } prevScrollLeft.current = left; + // We can only update the position when we guarantee that the render context has been + // rendered. This is achieved using ReactDOM.flushSync or when the context doesn't change. + let canUpdateInnerPosition = false; + if (nextRenderContext !== prevRenderContext.current || !prevRenderContext.current) { - setRenderContext(nextRenderContext); + // ReactDOM.flushSync cannot be called on `scroll` events fired inside effects + if (isUIEvent(event)) { + // To prevent flickering, the inner position can only be updated after the new context has + // been rendered. ReactDOM.flushSync ensures that the state changes will happen before + // updating the position. + ReactDOM.flushSync(() => { + setRenderContext(nextRenderContext); + }); + canUpdateInnerPosition = true; + } else { + setRenderContext(nextRenderContext); + } prevRenderContext.current = nextRenderContext; + } else { + canUpdateInnerPosition = true; } // Pass directly the render context to avoid waiting for the next render - if (nextRenderContext) { + if (nextRenderContext && canUpdateInnerPosition) { updateInnerPosition(nextRenderContext); } }, @@ -204,7 +232,6 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { renderContext, getColumns, isDragging: !!dragCol, - updateInnerPosition, getRootProps: (other = {}) => ({ style: rootStyle, ...other }), getInnerProps: () => ({ ref: handleInnerRef, 'aria-rowindex': 1, role: 'row' }), }; diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx index c681a1445228..ad266c61effa 100644 --- a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import * as ReactDOM from 'react-dom'; import { useForkRef } from '@mui/material/utils'; import { useGridApiContext } from '../../utils/useGridApiContext'; import { useGridRootProps } from '../../utils/useGridRootProps'; @@ -197,13 +198,18 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { ], ); + React.useLayoutEffect(() => { + if (renderContext) { + updateRenderZonePosition(renderContext); + } + }, [renderContext, updateRenderZonePosition]); + const updateRenderContext = React.useCallback( (nextRenderContext) => { setRenderContext(nextRenderContext); - updateRenderZonePosition(nextRenderContext); prevRenderContext.current = nextRenderContext; }, - [setRenderContext, prevRenderContext, updateRenderZonePosition], + [setRenderContext, prevRenderContext], ); React.useEffect(() => { @@ -212,7 +218,6 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { } const initialRenderContext = computeRenderContext(); - prevRenderContext.current = initialRenderContext; updateRenderContext(initialRenderContext); const { top, left } = scrollPosition.current!; @@ -257,14 +262,21 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { prevTotalWidth.current !== columnsTotalWidth; // TODO v6: rename event to a wider name, it's not only fired for row scrolling - apiRef.current.publishEvent(GridEvents.rowsScroll, { - top: scrollTop, - left: scrollLeft, - renderContext: shouldSetState ? nextRenderContext : prevRenderContext.current, - }); + apiRef.current.publishEvent( + GridEvents.rowsScroll, + { + top: scrollTop, + left: scrollLeft, + renderContext: shouldSetState ? nextRenderContext : prevRenderContext.current, + }, + event, + ); if (shouldSetState) { - updateRenderContext(nextRenderContext); + // Prevents batching render context changes + ReactDOM.flushSync(() => { + updateRenderContext(nextRenderContext); + }); prevTotalWidth.current = columnsTotalWidth; } }; diff --git a/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts b/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts index c7c406605292..3fb41f504cd8 100644 --- a/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts +++ b/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts @@ -207,7 +207,7 @@ export interface GridEventLookup }; // Scroll - rowsScroll: { params: GridScrollParams }; + rowsScroll: { params: GridScrollParams; event: React.UIEvent | MuiBaseEvent }; virtualScrollerContentSizeChange: {}; // Selection diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index e2a39725cbc3..70221d3f0ccb 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -60,8 +60,8 @@ "dayjs": "^1.10.7", "luxon": "^1.28.0 || ^2.0.0", "moment": "^2.29.1", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" }, "peerDependenciesMeta": { "date-fns": { diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 0945a85718b3..4c2d8e2179da 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -61,8 +61,8 @@ "dayjs": "^1.10.7", "luxon": "^1.28.0 || ^2.0.0", "moment": "^2.29.1", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" }, "peerDependenciesMeta": { "date-fns": { diff --git a/packages/x-license-pro/package.json b/packages/x-license-pro/package.json index c1ba0048a6c3..c3d1d3c50cae 100644 --- a/packages/x-license-pro/package.json +++ b/packages/x-license-pro/package.json @@ -39,7 +39,7 @@ "yargs": "^17.4.1" }, "peerDependencies": { - "react": "^17.0.2" + "react": "^17.0.2 || ^18.0.0" }, "setupFiles": [ "/src/setupTests.js"