Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGridPro] Fix onColumnWidthChange being called twice on autosize #12140

Merged

Conversation

shaharyar-shamshi
Copy link
Contributor

Fixes #12005

@shaharyar-shamshi shaharyar-shamshi changed the title fix onColumnWidthChange called before autosize affects column width [data grid] fix onColumnWidthChange called before autosize affects column width Feb 19, 2024
@mui-bot
Copy link

mui-bot commented Feb 19, 2024

Deploy preview: https://deploy-preview-12140--material-ui-x.netlify.app/

Generated by 🚫 dangerJS against e80c76a

Comment on lines 382 to 390
if (previousMouseClickEvent.current) {
const prevTimeStamp = previousMouseClickEvent.current.timeStamp;
const prevClientX = previousMouseClickEvent.current.clientX;
const prevClientY = previousMouseClickEvent.current.clientY;
if (
nativeEvent.timeStamp - prevTimeStamp < 300 &&
nativeEvent.clientX === prevClientX &&
prevClientY === nativeEvent.clientY
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the logic behind this logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So overall logic is.

Mouse double click is combination of 2 mouseup and mousedown event.

But according to our previous logic whenever mousedown event used to occur we add the event listeners to listen mouseup event and resize according.

This logic had a collision with the mouse double click so just to distinguish between mouse double click and mouse up/ mouse down event I compared the time interval between mouse down and mouseup event and their coordinate if it is same this directly means user wants to do double click.

Hope I am able to explain this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@romgrk added proper comment for understanding the code

@shaharyar-shamshi shaharyar-shamshi force-pushed the hotfix/fix-columnWidthChange-on-double-click branch from 2f3b2ea to 5c35631 Compare February 20, 2024 05:27
@zannager zannager added the component: data grid This is the name of the generic UI component, not the React module! label Feb 20, 2024
@shaharyar-shamshi shaharyar-shamshi force-pushed the hotfix/fix-columnWidthChange-on-double-click branch from 8b481c4 to 53f884a Compare February 21, 2024 18:26
@shaharyar-shamshi shaharyar-shamshi force-pushed the hotfix/fix-columnWidthChange-on-double-click branch from 53f884a to e3b0d63 Compare February 21, 2024 18:42
@shaharyar-shamshi
Copy link
Contributor Author

@romgrk Added the test case if you can review

@romgrk
Copy link
Contributor

romgrk commented Feb 26, 2024

I seem to be able to reproduce the original issue with this PR: https://codesandbox.io/p/sandbox/mui-mui-x-x-data-grid-forked-f9skhw?file=%2Fsrc%2Fdemo.tsx%3A113%2C1

@shaharyar-shamshi
Copy link
Contributor Author

I seem to be able to reproduce the original issue with this PR: https://codesandbox.io/p/sandbox/mui-mui-x-x-data-grid-forked-f9skhw?file=%2Fsrc%2Fdemo.tsx%3A113%2C1

It seems this is private I can't access this

@romgrk
Copy link
Contributor

romgrk commented Feb 26, 2024

Updated permissions.

@shaharyar-shamshi
Copy link
Contributor Author

shaharyar-shamshi commented Feb 26, 2024

Updated permissions.

Basically React state updates are asynchronous but this element key is the DOM element but state update hasn't yet propagated to the DOM before being accessed that's the reason we are getting the old value in the element key but new value elsewhere like in key colDef and width.

If you have any idea how in MUI we can pass the callback function to the setState so that I can get the latest dom element because I think current call signature of setState doesn't allow this.

@romgrk

@shaharyar-shamshi shaharyar-shamshi force-pushed the hotfix/fix-columnWidthChange-on-double-click branch from 0e5732d to 4bcd99b Compare February 26, 2024 22:23
@shaharyar-shamshi
Copy link
Contributor Author

shaharyar-shamshi commented Feb 26, 2024

Only way that I can figure out as of now to access the DOM element after state change is done in this commit

4bcd99b

But here I have to disable the eslint because I have to trigger the useEffect on change of isAutosizingRef.current but Mutable values like 'isAutosizingRef.current' aren't valid dependencies because mutating them doesn't re-render the component .

this disabling of the eslint will not cause any bug in future as I just need this to access the updated dom element.

To confirm this I updated the test case as well.

@romgrk if you can suggest any better approach

@romgrk
Copy link
Contributor

romgrk commented Feb 27, 2024

Wouldn't it be better to dispatch a columnWidthChange event in the autosizeColumns function?

@shaharyar-shamshi
Copy link
Contributor Author

Wouldn't it be better to dispatch a columnWidthChange event in the autosizeColumns function?

Problem with this approach is when we disptach the columnWidthChange event we have to access the DOM element as well but State update being asynchronous the width is still not propagated to DOM so we get outdated element.clientWidth

@romgrk
Copy link
Contributor

romgrk commented Feb 27, 2024

But the widths are available at gridColDef.width. The widths are set first in the state, then in the DOM, not the other way around.

@shaharyar-shamshi
Copy link
Contributor Author

But the widths are available at gridColDef.width. The widths are set first in the state, then in the DOM, not the other way around.

apiRef.current.publishEvent('columnWidthChange', {
        element: apiRef.current.getColumnHeaderElement(colDefRef.current.field),
        colDef: { ...colDefRef.current, width },
        width,
      });

here you can see we have to publish three key element, colDef, width here key element is the DOM element which is doesn't get updated immediately that the main reason I have to use useEffect to access the updated element after the DOM updation.

In your example also (https://codesandbox.io/p/sandbox/mui-mui-x-x-data-grid-forked-f9skhw?file=%2Fsrc%2Fdemo.tsx%3A76%2C6) the element.clientWidth was actually wrong not params.width.

@shaharyar-shamshi
Copy link
Contributor Author

I hope this is clear or should I make one short video explaining this

@shaharyar-shamshi shaharyar-shamshi force-pushed the hotfix/fix-columnWidthChange-on-double-click branch from ed1588f to d56332d Compare February 27, 2024 13:55
@shaharyar-shamshi shaharyar-shamshi force-pushed the hotfix/fix-columnWidthChange-on-double-click branch from c895e5c to 57a4903 Compare February 27, 2024 16:10
Comment on lines 726 to 734

newColumns.forEach((newColumn) => {
const width: number = newColumn.width as number;
apiRef.current.publishEvent('columnWidthChange', {
element: apiRef.current.getColumnHeaderElement(newColumn.field),
colDef: newColumn,
width,
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger for all columns regardless of if they have changed. You have the original columns in columns.

You can avoid typings like this, the inference knows it's a number:

const width: number = newColumn.width as number;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are doing something wrong this implementation is correct.

let me Explain in detail

There are two possibility of calling autosizeColumns function

  1. through doubleClick event Listener https://github.com/mui/mui-x/blob/next/packages/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx#L743 from here handleColumnSeparatorDoubleClick calls the autosizeColumns and over here it pass the exact column which is being resized
  2. At the time of mounting when user pass autoResizeOnMount prop https://github.com/mui/mui-x/blob/next/packages/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx#L716

here each of the column is being resized on mounting

In our case to solve this bug we only need to concentrate on case 1 when the autosizeColumns is called on double click.

So on double click event from https://github.com/mui/mui-x/blob/next/packages/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx#L661

we get only one column that needs to be resized and we need not check on all the columns.

Now in case 2 when autOResizeOnMount is called in that case all the columns should be resized and we need to decide whether to publish event or not.

So from both the case we can figure it out that either all of the column will be resized or only 1 will be resized.

if we want to disable the event publish on all column resize (case 2) we just need to add one condition

if (userOptions?.columns) {
          newColumns.forEach((newColumn) => {
            const width = newColumn.width;
            apiRef.current.publishEvent('columnWidthChange', {
              element: apiRef.current.getColumnHeaderElement(newColumn.field),
              colDef: newColumn,
              width,
            });
          });
        }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for an if guard, just check the array and dispatch one event for each column that actually changed. Compare newColumn.width !== columns[index].width in the .forEach loop to figure out which ones changed.

The autosize function is part of the public API and can be called programatically with any number of columns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okk then we need to consider this I thought it is not the public api

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@romgrk romgrk merged commit 80cbef7 into mui:next Feb 27, 2024
17 checks passed
@cherniavskii cherniavskii added plan: Pro Impact at least one Pro user CLA: signed See https://www.notion.so/mui-org/CLA-Contributor-License-Agreement-92ece655b1584b10b00e4de9e67eedb0 feature: Column resize labels Feb 27, 2024
@cherniavskii cherniavskii changed the title [data grid] fix onColumnWidthChange called before autosize affects column width [DataGridPro] Fix onColumnWidthChange being called twice on autosize Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA: signed See https://www.notion.so/mui-org/CLA-Contributor-License-Agreement-92ece655b1584b10b00e4de9e67eedb0 component: data grid This is the name of the generic UI component, not the React module! feature: Column resize plan: Pro Impact at least one Pro user
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[data grid] onColumnWidthChange called before autosize affects column width
5 participants