react使用Chart
安装
npm install @antv/g2 --save
使用
数据处理
鼠标指上去是否展示辅助线
设置图形每个通道的比例尺
设置图形的坐标轴
销毁
完整代码
import React, { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
import { Popover } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Chart } from '@antv/g2';
import styles from './index.less';
let chart:any;
const LineChart = forwardRef(({ lineList }: any,ref) => {
useImperativeHandle(ref, () => ({
handleClear,
}));
useEffect(() => {
if (lineList) {
let arr: any[] = [];
lineList.forEach((element: any) => {
let obj = {
periodDate: element.periodDate,
temperature: element.idxPosAmtFnl?element.idxPosAmtFnl/100:0, // 销售额
name: '销售额',
};
let item = {
periodDate: element.periodDate,
temperature: element.idxPosCntBsk, // 订单数
name: '订单数',
};
arr.push(obj);
arr.push(item);
});
chart = new Chart({
container: 'lineChart',
autoFit: true,
height: 100,
});
chart.data(arr);
chart.scale({
periodDate: {
range: [0, 1],
},
temperature: {
nice: true,
},
});
chart.tooltip({
showCrosshairs: true,
shared: true,
});
chart.axis('temperature', {
label: {
formatter: (val:string) => {
return val;
},
},
});
chart.line().position('periodDate*temperature').color('name').shape('smooth');
chart.point().position('periodDate*temperature').color('name').shape('circle');
chart.legend(false); //关闭底部内容
chart.render();
}
}, [lineList]);
const handleClear = ()=>{
!!chart && chart?.destroy();
chart = null;
}
return (
<div className={styles.lineChart}>
<div className={styles.lineChartHead}>
<div>
销售额/订单数
<Popover content="当前门店30天内的销售额和订单数趋势数据">
<ExclamationCircleOutlined />
</Popover>
</div>
<div>30天趋势图</div>
</div>
<div className={styles.chart}>
<div id="lineChart"></div>
</div>
</div>
);
});
export default LineChart;
页面使用
<LineChart ref={chartRef} lineList={data?.lineList} />
请求时销毁
const handleChartFn = ()=>{
chartRef && chartRef?.current?.handleClear();
}
问题
请求之后出现重叠问题
解决:请求之后没有销毁导致又创建了一个chart,每次请求的时候判断有chart就销毁。
使用react的forwardRef方法,在父组件请求时调用销毁方法
没有创建chart就销毁会报错。
!重大问题:
优化:
我的代码场景是页面有个刷新按钮,在页面10分钟可以请求一次,结果上面的写法正确请求没有问题,在页面停留很长一段时间后会导致饼图出现重影,折线图没有问题。上面的代码还在函数外定义了chart结果,我在useEffect里又定义了一个chart,所以导致chart没被销毁
优化代码:使用useRef
饼图代码:
import React, { useEffect, useImperativeHandle, forwardRef, useRef } from 'react';
import { Card, Popover } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Chart } from '@antv/g2';
import styles from './index.less';
const StatisticsCard = forwardRef(
({ title, id, unit, total = 0, num = 0, online = 0, offline = 0, todayMembers = 0, tips = '' }: any, ref) => {
// 使用ref获取容器元素
const containerRef:any = useRef(null);
useImperativeHandle(ref, () => ({
handleClear,
}));
useEffect(() => {
if (id == 3) {
return;
}
const data = [
{ item: '线上', percent: online },
{ item: '线下', percent: offline },
];
const chart = new Chart({
container: containerRef.current,
autoFit: true,
});
chart.data(data);
chart.coordinate('theta', {
radius: 0.85,
});
chart.scale('percent', {
formatter: (val) => {
return val;
},
});
chart.tooltip({
showTitle: false,
showMarkers: false,
});
chart.axis(false); // 关闭坐标轴
const interval = chart
.interval()
.adjust('stack')
.position('percent')
.color('item')
.label('item', (val) => {
if (!!online && !!offline) {
return {
offset: -15,
style: {
opacity: 1,
fill: 'white',
fontSize: 12,
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)',
},
content: (obj) => {
// if (!!obj.percent) {
// return obj.item + '\n' + obj.percent;
// }
return '';
},
};
} else {
return {
// offset: -50,
offsetY: -50,
style: {
textAlign: 'center',
opacity: 1,
fill: 'white',
fontSize: 12,
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)',
},
content: (obj) => {
// if (!!obj.percent) {
// return obj.item + '\n' + obj.percent;
// }
return '';
},
};
}
})
.tooltip('item*percent', (item, percent) => {
percent = percent + unit;
return {
name: item,
value: percent,
};
})
.style({
lineWidth: 1,
stroke: '#fff',
});
chart.interaction('element-single-selected');
// chart.legend(false); //关闭底部内容
chart.render();
// 默认选择
interval.elements[0].setState('selected', true);
// 返回清理函数,在组件卸载时销毁图表实例,避免内存泄漏
return () => {
chart.destroy();
};
}, [offline]);
const handleClear = () => {
// console.log(chart, 'chart');
// !!chart && chart?.destroy();
// chart = null;
};
return (
<Card className={styles.statisticsCard}>
<div className={styles.head}>
<div>{title}</div>
<Popover content={tips}>
<ExclamationCircleOutlined />
</Popover>
</div>
<div className={id == 3 ? `${styles.content} ${styles.flex}` : `${styles.content}`}>
<div>
<div className={styles.num}>
{total}
{unit}
</div>
{id == 3 ? (
<div className={styles.mt10}>总会员数</div>
) : (
<div className={styles.red}>
昨日全天:{num}
{unit}
</div>
)}
</div>
<div>
{id == 3 ? (
<div className={styles.contentRight}>
<div>
{todayMembers}
{unit}
</div>
<div className={styles.add}>今日新增数</div>
<div className={styles.redText}>
昨日新增:{num}
{unit}
</div>
</div>
) : (
<div className={styles.container} ref={containerRef} id={`container${id}`}></div>
)}
</div>
</div>
</Card>
);
},
);
export default StatisticsCard;
这样写法就不需要传递方法出去销毁chart了,还是多使用useRef吧