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
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '../hooks/useAxis';
import { useZAxes } from '../hooks/useZAxis';
import {
compareTooltipAxes,
selectorChartsInteractionTooltipXAxes,
selectorChartsInteractionTooltipYAxes,
UseChartCartesianAxisSignature,
Expand Down Expand Up @@ -115,10 +116,25 @@ export function useAxisTooltip(

const store = useStore<[UseChartCartesianAxisSignature]>();

const tooltipXAxes = useSelector(store, selectorChartsInteractionTooltipXAxes);
const tooltipYAxes = useSelector(store, selectorChartsInteractionTooltipYAxes);

const tooltipRotationAxes = useSelector(store, selectorChartsInteractionTooltipRotationAxes);
const tooltipXAxes = useSelector(
store,
selectorChartsInteractionTooltipXAxes,
[],
compareTooltipAxes,
);
const tooltipYAxes = useSelector(
store,
selectorChartsInteractionTooltipYAxes,
[],
compareTooltipAxes,
);

const tooltipRotationAxes = useSelector(
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.

);

const series = useSeries();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,13 @@ export const selectorChartsInteractionAxisTooltip = createSelector(
[selectorChartsInteractionTooltipXAxes, selectorChartsInteractionTooltipYAxes],
(xTooltip, yTooltip) => xTooltip.length > 0 || yTooltip.length > 0,
);

type AxisIdentifier = { axisId: string; dataIndex: number };

export function compareTooltipAxes(a: AxisIdentifier[], b: AxisIdentifier[]) {
if (a.length !== b.length) {
return false;
}

return a.every((_, i) => a[i].axisId === b[i].axisId && a[i].dataIndex === b[i].dataIndex);
}
Loading