BK52
BK52

Reputation: 934

Recharts custom label render every time when mouse over

I create custom label for PieChart

CustomLabel.js

const renderCustomizedLabel = (props,centerText) => {
    console.log("rendered")
    return (
        <g>...</g>
    );
};

export default renderCustomizedLabel;

CustomPieChart.js

export default class Example extends PureComponent {
    constructor(props){
        super(props);
    }
    render() {
        return (
            <ResponsiveContainer width="100%" aspect={2}>
                <PieChart width={600} height={600}>
                    <Pie  
                     data={this.props.data} 
                     dataKey="value" 
                     nameKey="name" 
                     cx="50%" cy="50%" innerRadius={80} outerRadius={90}
                     label={(a)=>CustomPieChartLabel(a,this.props.centerText)}>
                     {this.props.data.map((entry, index) => (<Cell key={`cell-${index}`} fill={entry.color} />))}
                     </Pie>
                </PieChart>
             </ResponsiveContainer> 
        );
    }
}

Problem

When mouse over the cell every time, renderCustomizedLabel works and render for each data.

In the code above I didn't use any onMouseMove, onMouseOver, onMouseEnter methods.

enter image description here

Like in the picture above when mouse over red, blue or grey zone, console.log("rendered") works 3 times.

For solve this I try to use React.memo

CustomLabelWithMemo.js

const MemoComponent = React.memo(function renderCustomizedLabel(props) {
    console.log("rendered")
    return (
        <g>...</g>
    );
});

export default MemoComponent

But it gives me an error

TypeError: Object(...) is not a function

How can I solve this problem?

Reproduction link CodeSandBox

Upvotes: 2

Views: 4564

Answers (1)

bas
bas

Reputation: 15722

I think the main problem from looking at the sandbox is that you're still using MemoComponent as if it were a regular function, instead of a component when passing it to the label prop of your Pie component.

You do this:

label={(a) => CustomPieChartLabel(a, this.props.centerText)}

Instead you could do something like this:

label={<CustomPieChartLabel centerText={this.props.centerText} />}

I've also slightly adjusted your CustomPieChartLabel.js file:

import React from "react";

const RADIAN = Math.PI / 180;

const renderCustomizedLabel = (props) => {
  console.log("rendered");
  const {
    cx,
    cy,
    midAngle,
    outerRadius,
    fill,
    payload,
    percent,
    value,
    centerText
  } = props;
  const sin = Math.sin(-RADIAN * midAngle);
  const cos = Math.cos(-RADIAN * midAngle);
  const sx = cx + (outerRadius + 10) * cos;
  const sy = cy + (outerRadius + 10) * sin;
  const mx = cx + (outerRadius + 30) * cos;
  const my = cy + (outerRadius + 30) * sin;
  const ex = mx + (cos >= 0 ? 1 : -1) * 30;
  const ey = my;
  const textAnchor = cos >= 0 ? "start" : "end";
  return (
    <g>
      <text x={cx} y={cy} textAnchor="middle" fill={fill}>
        {centerText.title}
      </text>
      <text x={cx} y={cy} dy={20} textAnchor="middle" fill={fill}>
        {centerText.value}
      </text>

      <path
        d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
        stroke={fill}
        fill="none"
      />
      <circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
      <text
        style={{ fontWeight: "bold" }}
        x={ex + (cos >= 0 ? 1 : -1) * 12}
        y={ey}
        textAnchor={textAnchor}
        fill={fill}
      >
        {payload.name}
      </text>
      <text
        x={ex + (cos >= 0 ? 1 : -1) * 12}
        y={ey}
        dy={18}
        textAnchor={textAnchor}
        fill="#999"
      >
        {value}
      </text>
    </g>
  );
};

const CustomPieChartLabel = React.memo(renderCustomizedLabel);

export default CustomPieChartLabel;

Sandbox example

Upvotes: 3

Related Questions