我有一个代表一些数据的图表。该图表可以在两种类型的单位(Mwh 和 Kwh)之间切换。 当数据初始化加载时,它们是 100% 正确的,并且被放置在需要的位置。 但切换之后,它会产生一些“问题”。 作为系列选项传递的数据,它们是正确的,但图中的块放置不正确。
有问题截图:
我尝试使用静态数据,未排序,但仍然存在同样的问题。 另外,我尝试重新绘制图表但没有帮助。
这是部分代码:
const setBuildingNamesAndDataForChart = (sortedData: BuildingData[]) => {
const tempNames: string[] = [];
const districtHeatings: number[] = [];
const districtCoolings: number[] = [];
const gasImports: number[] = [];
const electricityDemands: number[] = [];
const electricityGenerations: number[] = [];
const co2Uses: number[] = [];
const energyUses: number[] = [];
sortedData.forEach(
({
building,
year,
USED_HEATING_IMPORT,
DISTRICT_HEATING_IMPORT,
USED_COOLING_IMPORT,
DISTRICT_COOLING_IMPORT,
USED_GAS_IMPORT,
GAS_IMPORT_KWH,
USED_ELECTRICITY_DEMAND,
ELECTRICITY_DEMAND,
USED_ELECTRICITY_GENERATION,
ELECTRICITY_GENERATION,
CO2_USE,
}) => {
tempNames.push(`${building.buildingName} ${year}`);
districtHeatings.push(
parseFloat(
(toggleState
? USED_HEATING_IMPORT ?? 0
: (DISTRICT_HEATING_IMPORT ?? 0) / 1000
).toFixed(1)
)
);
districtCoolings.push(
parseFloat(
(toggleState
? USED_COOLING_IMPORT ?? 0
: (DISTRICT_COOLING_IMPORT ?? 0) / 1000
).toFixed(1)
)
);
gasImports.push(
parseFloat(
(toggleState
? USED_GAS_IMPORT ?? 0
: (GAS_IMPORT_KWH ?? 0) / 1000
).toFixed(1)
)
);
electricityDemands.push(
parseFloat(
(toggleState
? USED_ELECTRICITY_DEMAND ?? 0
: (ELECTRICITY_DEMAND ?? 0) / 1000
).toFixed(1)
)
);
electricityGenerations.push(
parseFloat(
(toggleState
? (USED_ELECTRICITY_GENERATION ?? 0) * -1
: (ELECTRICITY_GENERATION ?? 0) / -1000
).toFixed(1)
)
);
co2Uses.push(parseFloat((CO2_USE ?? 0).toFixed(1)));
}
);
setBuildingNames(tempNames);
setDataForChart([
districtHeatings,
districtCoolings,
gasImports,
electricityDemands,
electricityGenerations,
co2Uses,
energyUses,
]);
};
const sortOnToggle = (data: BuildingData[]): BuildingData[] => {
return [...data].sort((a, b) => {
const sumA = toggleState
? (a.USED_HEATING_IMPORT ?? 0) +
(a.USED_COOLING_IMPORT ?? 0) +
(a.USED_GAS_IMPORT ?? 0) +
(a.USED_ELECTRICITY_DEMAND ?? 0) +
(a.USED_ELECTRICITY_GENERATION ?? 0)
: (a.DISTRICT_HEATING_IMPORT ?? 0) +
(a.DISTRICT_COOLING_IMPORT ?? 0) +
(a.GAS_IMPORT_KWH ?? 0) +
(a.ELECTRICITY_DEMAND ?? 0) +
(a.ELECTRICITY_GENERATION ?? 0);
const sumB = toggleState
? (b.USED_HEATING_IMPORT ?? 0) +
(b.USED_COOLING_IMPORT ?? 0) +
(b.USED_GAS_IMPORT ?? 0) +
(b.USED_ELECTRICITY_DEMAND ?? 0) +
(b.USED_ELECTRICITY_GENERATION ?? 0)
: (b.DISTRICT_HEATING_IMPORT ?? 0) +
(b.DISTRICT_COOLING_IMPORT ?? 0) +
(b.GAS_IMPORT_KWH ?? 0) +
(b.ELECTRICITY_DEMAND ?? 0) +
(b.ELECTRICITY_GENERATION ?? 0);
return sumB - sumA;
});
};
const reflowCharts = () => {
Highcharts.charts.forEach((chart) => chart?.reflow());
};
useEffect(() => {
if (data) {
const sorted = co2
? [...data].sort((a, b) => b.CO2_USE - a.CO2_USE)
: sortOnToggle(data);
setBuildingNamesAndDataForChart(sorted);
}
}, [data, co2, toggleState]);
useEffect(() => {
setTimeout(() => {
reflowCharts();
}, 10);
}, []);
const options = {
chart: {
type: "column",
polar: false,
},
exporting: {
...chartExportOption,
chartOptions: {
title: {
text: `${
co2 ? t("co2EmissionPerBuilding") : t("energyUsePerBuilding")
}`,
},
},
},
plotOptions: {
series: {
stacking: "normal",
},
},
title: {
useHTML: true,
text: `${co2 ? t("co2EmissionPerBuilding") : t("energyUsePerBuilding")}
<span class="custom-tooltip custom-tooltip-left" id="question">?<span class="tooltip-text">${
co2 ? t("h4") : t("h9")
}</span>
</span>
`,
align: "center",
style: {
fontFamily: "Inter, sans-serif",
fontSize: "15px",
fontWeight: "600",
color: "#333333",
},
},
subtitle: {
text: co2
? t("mostRecentCo2UseAvailable")
: t("mostRecentEnergyUseAvailable"),
},
series: co2
? [
{
data: dataForChart[5],
name: t("co2Use"),
color: colorScheme3.renorBlue,
turboThreshold: 0,
showInLegend: haveValues(dataForChart[5] ?? []),
},
]
: [
{
data: dataForChart[0],
name: t("districtHeating"),
color: colorScheme3.renorRed,
turboThreshold: 0,
rounded: false,
showInLegend: haveValues(dataForChart[0] ?? []),
},
{
data: dataForChart[1],
name: t("districtCooling"),
color: colorScheme3.renorBlue,
turboThreshold: 0,
showInLegend: haveValues(dataForChart[1] ?? []),
},
{
data: dataForChart[2],
name: t("gas"),
color: colorScheme3.renorMediumGray,
turboThreshold: 0,
showInLegend: haveValues(dataForChart[2] ?? []),
},
{
data: dataForChart[3],
name: t("electricity"),
color: colorScheme3.renorGreen,
turboThreshold: 0,
showInLegend: haveValues(dataForChart[3] ?? []),
},
{
data: dataForChart[4],
name: t("electricityGenerated"),
color: colorScheme3.renorYellow,
turboThreshold: 0,
showInLegend: haveValues(dataForChart[4] ?? []),
},
],
legend: {
enabled: true,
verticalAlign: "top",
},
tooltip: {
formatter() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const point: any = this;
const pointsArray = point?.points;
const seriesName = point?.x.split(" ");
const lastPart = seriesName?.pop();
const firstPart = seriesName?.join(" ");
const returningData = pointsArray.map((el: any) => {
const valueToShow = el?.point?.y >= 0 ? el?.point?.y : -el?.point?.y; // Convert negative value back to positive for tooltip display
if (el?.point?.y !== 0)
return `<span style="color:${el.point?.color}" >${
el.point?.series.name
}</span>: <b>${parseFloat(valueToShow?.toFixed(1))?.toLocaleString(
"de"
)}</b> ${co2 ? " kg" : !toggleState ? " MWh" : " kWh/m²"}<br/>`;
});
return `<span> ${firstPart}</span><br/>${t(
"year"
)}: ${lastPart}<br/> ${returningData.join("")}`;
},
shared: true,
},
credits: {
text: "© Renor",
href: "www.renor.nl",
},
pane: {
background: [],
},
responsive: {
rules: [],
},
yAxis: [
{
title: {
text: co2
? t("co2Cons") + " [kg/" + t("yearSingular_nocaps") + "]"
: !toggleState
? t("energyUse") + " MWh/" + t("yearSingular_nocaps")
: t("energyUse") + " kWh/m²." + t("yearSingular_nocaps"),
},
labels: {
formatter: function () {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const point: any = this;
return formatYAxisLabel(point.value);
},
},
},
],
xAxis: [
{
categories: buildingNames,
reversed: false,
visible: data.length < 25,
type: "category",
title: { text: " " },
labels: {
formatter() {
if (data.length === 0) return "";
// eslint-disable-next-line @typescript-eslint/no-this-alias
const point: any = this;
const valueArray = point?.value?.split(" ");
const lastValue = valueArray?.pop();
const firstPart = valueArray?.join(" ");
return `<span> ${firstPart}</span>`;
},
},
},
],
lang: chartDownloadOptionsTranslations(language || "nl"),
};```
更新图表选项的最佳方法是将它们保留在 useState 挂钩内。更新状态时,仅将新选项传递给
chart.update
方法。
演示: https://stackblitz.com/edit/react-jbcrlq?file=index.js
API: https://github.com/highcharts/highcharts-react?tab=readme-ov-file#optimal-way-to-update