Skip to content

[charts] Improve axis tooltip performances #17398

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

Merged
merged 5 commits into from
Apr 23, 2025

Conversation

alexfauquette
Copy link
Member

@alexfauquette alexfauquette commented Apr 16, 2025

Fix #17367

Axis tooltip content is re-rendering a log because of similar issues as the one in #16575

Here I don't see a way to fix it except by doing a quick JS comparison if the array to return the same ref if value do not change.

Why it broke

The root issue comes from the x/y position update triggers a computation of the axes indexes (which is expected) but if indexes are unchanged, it should not cause further updates.

It's the case for single axis manipulation. For example the axis highlight, because we manipulate numbers. So when the selector returns 2 as dataIndex the process stops as long as the value stay the same.

But now tooltip suports multiple axes, so when the selector returns [{ axisId: "A", dataIndex: 2 }] the array reference is always a new one triggering the entire pipeline

After the fix

Capture.video.du.2025-04-16.12-16-20.mp4

@alexfauquette alexfauquette added performance component: charts This is the name of the generic UI component, not the React module! labels Apr 16, 2025
Copy link

github-actions bot commented Apr 16, 2025

Thanks for adding a type label to the PR! 👍

Copy link

Please add one type label to categorize the purpose of this PR appropriately:

docs, release, bug, regression, maintenance, dependencies, enhancement or new feature

@mui-bot
Copy link

mui-bot commented Apr 16, 2025

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

Generated by 🚫 dangerJS against 58d748a

store,
selectorChartsInteractionTooltipRotationAxes,
[],
compareTooltipAxes,
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to use isDeepEqual instead?

Otherwise, it's easy to forget to update compareTooltipAxes if we add more properties.

Copy link
Member Author

Choose a reason for hiding this comment

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

Otherwise, it's easy to forget to update compareTooltipAxes if we add more properties.

I assume TS should prevent use from that issue. I updated the selector definition to make sure the type is enfoced on both side.

My issue with isDeepEqual is the ease of use. It would be tempting to put that every where.
I prefer a custom funciton, used only on simple usecases when necessary.

Copy link
Member

Choose a reason for hiding this comment

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

My issue with isDeepEqual is the ease of use. It would be tempting to put that every where.

I agree with that feeling, but I feel the risk of forgetting to add a prop to compareTooltipAxes is greater than using isDeepEqual. It's a matter of personal opinion, so it isn't a blocker.

If we want to avoid using isDeepEqual everywhere we can add a comment explaining why it's ok to use it in this case, and require an explanation from now on whenever we need to use it. Do you think that would be acceptable?

Copy link
Member Author

Choose a reason for hiding this comment

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

That would be a solution. I let the PR open for the week-end to discuss with @JCQuintas if he think about another solution to memorize this selector

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can pass the function to the third argument of createSelector as

export const selectorChartsInteractionTooltipXAxes = createSelector(
  [selectorChartsInteractionPointerX, selectorChartXAxis],
  (value, axes) => {
    if (value === null) {
      return [];
    }

    return axes.axisIds
      .filter((id) => axes.axis[id].triggerTooltip)
      .map(
        (axisId): AxisItemIdentifier => ({
          axisId,
          dataIndex: getAxisIndex(axes.axis[axisId], value),
        }),
      )
      .filter(({ dataIndex }) => dataIndex >= 0);
  },
  {
    memoizeOptions: {
      resultEqualityCheck: compareTooltipAxes,
    },
  },
);

to prevent exporting the func? This would also be advantageous as we wouldn't need to remember to add it when using the selector itself.

Other than that I don't have much preference to which we use, but I do agree isDeepEqual is more complete.

@alexfauquette alexfauquette added the bug 🐛 Something doesn't work label Apr 16, 2025
Copy link

codspeed-hq bot commented Apr 16, 2025

CodSpeed Performance Report

Merging #17398 will not alter performance

Comparing alexfauquette:fix-axis-tooltip-pref (58d748a) with master (f7b122d)

Summary

✅ 8 untouched benchmarks

@alexfauquette alexfauquette enabled auto-merge (squash) April 23, 2025 07:46
@alexfauquette alexfauquette merged commit 33e0b7f into mui:master Apr 23, 2025
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: charts This is the name of the generic UI component, not the React module! performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[charts][regression] Performance regression on upgrade to v8 in value formatter
4 participants