저번 포스팅 때, Legend에 hover했을 때, 해당 데이터만 highlight 되도록 구현을 했다.

이번에는 Legend를 커스터마이징하여 색상도 바꿔보도록 하려고한다.

1. CustomLegend 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const CustomLegend = (props: any) => {
const { payload, onMouseEnter, onMouseLeave } = props;

return (
<ul>
{payload.map((entry: any, index: any) => (
<li
key={`item-${index}`}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
style={{ listStyle: "none", color: colors.GRAPH[`${index + 1}`] }}
>
{entry.value}
</li>
))}
</ul>
);
};
  • 예시를 위해 타입은 any로 설정하였다.
  • Legend의 각 li에 mouse 이벤트를 할당하였다.
  • 마우스 이벤트는 호버된 데이터를 제외한 데이터들의 opacity를 줄여서 해당 데이터만 highlight 되도록 한다.

2. CustomLegend의 props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const handleMouseEnter = (o: any) => {
const dataKey = o.target.innerHTML;

const entries = Object.entries(opacity).map(([key, value]) =>
key === dataKey ? [key, 1] : [key, 0.2]
);

const mappedObj: any = entries.reduce((prev, curr) => {
const [key, value] = curr;
prev = { ...prev, [key]: value };
return prev;
}, {});

setOpacity(mappedObj);
};

그런데, 해당 데이터에 호버를 해도 모든 데이터의 opacity가 줄어드는 문제가 발생했다.

그 이유는 Legend에서의 props와 customLegend의 props가 달라서 mouse 이벤트가 잘못 동작했기 때문이다.

  • 이전 mouse 이벤트에서는 props안에 dataKey 속성으로 호버된 데이터 값을 가져올 수 있었다.
  • 하지만 customLegend에서는 props에 너무나도 많은 속성이 있었고 이 중 나는 target속성의 innerHTML 속성으로 호버된 데이터의 dataKey를 확인하는 로직을 구성하였다.

3. 전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { useEffect, useState } from "react";
import {
LineChart,
Line,
Tooltip,
Legend,
ResponsiveContainer,
} from "recharts";
import colors from "styles/colors";

const CustomLegend = (props: any) => {
const { payload, onMouseEnter, onMouseLeave } = props;

return (
<ul>
{payload.map((entry: any, index: any) => (
<li
key={`item-${index}`}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
style={{ listStyle: "none", color: colors.GRAPH[`${index + 1}`] }}
>
{entry.value}
</li>
))}
</ul>
);
};

function NewPortChart() {
const [opacity, setOpacity] = useState<any>({});

const handleMouseEnter = (o: any) => {
const dataKey = o.target.innerHTML;

const entries = Object.entries(opacity).map(([key, value]) =>
key === dataKey ? [key, 1] : [key, 0.2]
);

const mappedObj: any = entries.reduce((prev, curr) => {
const [key, value] = curr;
prev = { ...prev, [key]: value };
return prev;
}, {});

setOpacity(mappedObj);
};

const handleMouseLeave = () => {
const entries = Object.entries(opacity).map(([key, value]) => [key, 1]);

const mappedObj: any = entries.reduce((prev, curr) => {
const [key, value] = curr;
prev = { ...prev, [key]: value };
return prev;
}, {});

setOpacity(mappedObj);
};

useEffect(() => {
const mappedOpacity = Object.keys(data[0]).reduce((prev, curr) => {
prev = { ...prev, [curr]: 1 };
return prev;
}, {});
setOpacity(mappedOpacity);
}, []);

return (
<ResponsiveContainer width="100%" height="100%">
<LineChart width={857} height={440} data={data}>
<Tooltip />
<Legend
align="right"
verticalAlign="middle"
layout="vertical"
content={
<CustomLegend
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
/>
}
/>
{Object.keys(data[0]).map((key, index) => (
<Line
key={key}
type="linear"
dataKey={key}
strokeOpacity={opacity[key]}
strokeLinecap="round"
stroke={colors.GRAPH[`${index + 1}`]}
activeDot={false}
dot={false}
/>
))}
</LineChart>
</ResponsiveContainer>
);
}

export default NewPortChart;

기본 CustomLegend

기본 customlegend

Hover된 데이터만 highlight

hover1
hover2
hover3

추가로…

색상만 바꿀 것이였다면 왜 CustomLegend까지 쓰면서 복잡하게 시도를 했을까 궁금증이 들 수도 있다.

디자이너 요구사항이 Legend와 해당 Line 데이터 끝 부분을 선으로 연결해달라는 요청이 있었기 때문에 CustomLegend를 사용해보았다.

아직 해당 부분은 좀 더 고민이 필요하기 때문에 추후에 포스팅하도록 하겠다.