Highcharts React Pie Chart rendering multiple time - javascript

My code this
import React, { useEffect, useRef, useState } from "react";
import * as Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
export const PieChart = (props: any) => {
const [chartIsLoaded, setChartIsLoaded] = useState(false);
const series: any = [{
innerSize: '80%',
name: props.name,
colorByPoint: true,
data: props.chartData
}]
useEffect(() => {
setChartIsLoaded(true);
}, [])
const chartComponentRef = useRef<HighchartsReact.RefObject>(null);
const options: Highcharts.Options = {
chart: {
backgroundColor: "#0c0c0c",
borderColor: "#0c0c0c",
plotBorderWidth: 0,
plotShadow: false,
type: 'pie',
height: "70%",
},
title: {
style : {
display : 'none'
}
},
tooltip: {
pointFormat: '<b>{point.percentage:.1f}%</b>',
backgroundColor: "#1B1B1B",
borderColor: "transparent",
valueDecimals: 2,
borderRadius: 0,
style: {
color: "#fff",
fontSize: "15px"
}
},
accessibility: {
point: {
valueSuffix: '%'
}
},
legend: {
itemStyle: {color: "#fff", fontWeight: "400", fontFamily: "teragon-sans"}
},
plotOptions: {
pie: {
borderColor: "#0c0c0c",
borderWidth: 6,
allowPointSelect: true,
color: "#fff",
cursor: 'pointer',
dataLabels: {
enabled: false,
},
showInLegend: true
}
},
series: series
};
return (
<div>
{chartIsLoaded &&
<HighchartsReact
highcharts={Highcharts}
options={options}
ref={chartComponentRef}
oneToOne={true}
/>
}
</div>
);
};
chartData coming from this code:
let data = sheetData[0].data;
let invesment = await groupData(data, "Investment Type");
Problem: Chart rendering multiple times. Also, I have Bar Chart it's happening on that. There is no problem with Line Chart. The data is preparing with reduce function but its async waiting awaits. Moreover I tried with promises. Unfortunately, It was rendered multiple times again. How can I fix this situation?

Your chart options are initiated after every component update which directs to the chart update on every component update. I recommend you to keep chart options in a stare or memorize them. For example:
const PieChart = ({ name, chartData }) => {
const [chartOptions, setChartOptions] = useState(false);
useEffect(() => {
const series = [
{
innerSize: "80%",
name,
colorByPoint: true,
data: chartData
}
];
const options = {
...,
series
};
if (chartData) {
setChartOptions(options);
}
}, [chartData]);
return (
<div>
{chartOptions && (
<HighchartsReact highcharts={Highcharts} options={chartOptions} />
)}
</div>
);
};
Live demo: https://codesandbox.io/s/highcharts-react-demo-fz6rr6?file=/demo.jsx
Docs: https://www.npmjs.com/package/highcharts-react-official#optimal-way-to-update

Related

I can't able to remove and redraw the graph in react highcharts

Simple problem,
I can't able to remove and redraw the chart.
I have not found any solution for react highchart.
here is my code
import { useState, useEffect, useRef } from "react";
import Highcharts from "highcharts/highstock";
import HighchartsReact from "highcharts-react-official";
export default function IndexPage() {
const chartComponent = useRef(null);
const [options] = useState({
chart: {
renderTo: "chart",
type: "spline",
zoomType: "x",
backgroundColor: "transparent",
plotBorderColor: "transparent"
},
title: {
text: "Chart"
},
xAxis: {
type: "datetime",
tickPixelInterval: 200,
gridLineColor: "red",
labels: {
style: {
color: "red"
}
},
lineColor: "red",
minorGridLineColor: "red",
tickColor: "red",
title: {
style: {
color: "red"
}
}
},
yAxis: {
title: {
text: ""
},
gridLineColor: "red",
labels: {
style: {
color: "red"
}
},
lineColor: "red",
minorGridLineColor: "red",
tickColor: "red"
},
credits: {
enabled: false
},
series: [
{
name: "Random Number"
}
]
});
useEffect(() => {
const interval = setInterval(async () => {
if (chartComponent.current) {
chartComponent.current.chart.series[0].addPoint(
[new Date().getTime(), Math.floor(Math.random() * 100)],
true
);
}
}, 2000);
return () => clearInterval(interval);
}, []);
function clearChart() {
if (chartComponent.current) {
chartComponent.current.chart.series[0].remove(false);
chartComponent.current.chart.redraw();
}
}
return (
<>
<HighchartsReact
ref={chartComponent}
highcharts={Highcharts}
options={options}
/>
<center>
<button onClick={clearChart}>Clear Chart</button>
</center>
</>
);
}
I am using this code for redraw the graph -
function clearChart() {
if (chartComponent.current) {
chartComponent.current.chart.series[0].remove(false);
chartComponent.current.chart.redraw();
}
}
But redraw function is not redrawing the graph, hence I am getting error. because loop still try to push value in not existing graph.
here is the codesandbox project https://codesandbox.io/s/sad-fire-lms4nq
You need to clear the interval in each clearChart function call. You can do that as below:
export default function IndexPage() {
const chartComponent = useRef(null);
const [hasSeries, setHasSeries] = useState(true);
const [options] = useState({...});
useEffect(() => {
const interval = setInterval(async () => {
if (hasSeries && chartComponent.current) {
chartComponent.current.chart.series[0].addPoint(
[new Date().getTime(), Math.floor(Math.random() * 100)],
true
);
}
}, 2000);
return () => clearInterval(interval);
}, [hasSeries, chartComponent]);
function clearChart() {
if (chartComponent.current) {
setHasSeries(false);
chartComponent.current.chart.series[0].remove(false);
chartComponent.current.chart.redraw();
}
}
return (
<>
<HighchartsReact
ref={chartComponent}
highcharts={Highcharts}
options={options}
/>
<center>
<button onClick={clearChart}>Clear Chart</button>
</center>
</>
);
}
Live demo: https://codesandbox.io/s/sweet-blackwell-p3k31-p3k31y?file=/pages/index.js
Useful article: https://www.codementor.io/#damianpereira/how-to-use-clearinterval-inside-react-s-useeffect-and-why-it-is-important-1si7mztjlk

How to hide the legend in chart.js in a react project?

I am trying to hide the legend of my chart created with Chart.js.
According to the official documentation (https://www.chartjs.org/docs/latest/configuration/legend.html), to hide the legend, the display property of the options.display object must be set to false.
I have tried to do it in the following way:
const options = {
legend: {
display: false,
}
};
But it doesn't work, my legend is still there. I even tried this other way, but unfortunately, without success.
const options = {
legend: {
display: false,
labels: {
display: false
}
}
}
};
This is my full code.
import React, { useEffect, useState } from 'react';
import { Line } from "react-chartjs-2";
import numeral from 'numeral';
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 1,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,000");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "DD/MM/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
callback: function(value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
const buildChartData = (data, casesType = "cases") => {
let chartData = [];
let lastDataPoint;
for(let date in data.cases) {
if (lastDataPoint) {
let newDataPoint = {
x: date,
y: data[casesType][date] - lastDataPoint
}
chartData.push(newDataPoint);
}
lastDataPoint = data[casesType][date];
}
return chartData;
};
function LineGraph({ casesType }) {
const [data, setData] = useState({});
useEffect(() => {
const fetchData = async() => {
await fetch("https://disease.sh/v3/covid-19/historical/all?lastdays=120")
.then ((response) => {
return response.json();
})
.then((data) => {
let chartData = buildChartData(data, casesType);
setData(chartData);
});
};
fetchData();
}, [casesType]);
return (
<div>
{data?.length > 0 && (
<Line
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data
},
],
}}
options={options}
/>
)}
</div>
);
}
export default LineGraph;
Could someone help me? Thank you in advance!
PD: Maybe is useful to try to find a solution, but I get 'undefined' in the text of my legend and when I try to change the text like this, the text legend still appearing as 'Undefindex'.
const options = {
legend: {
display: true,
text: 'Hello!'
}
};
As described in the documentation you linked the namespace where the legend is configured is: options.plugins.legend, if you put it there it will work:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
}
]
},
options: {
plugins: {
legend: {
display: false
}
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.0/chart.js"></script>
</body>
On another note, a big part of your options object is wrong, its in V2 syntax while you are using v3, please take a look at the migration guide
Reason why you get undefined as text in your legend is, is because you dont supply any label argument in your dataset.
in the newest versions this code works fine
const options = {
plugins: {
legend: {
display: false,
},
},
};
return <Doughnut data={data} options={options} />;
Import your options value inside the charts component like so:
const options = {
legend: {
display: false
}
};
<Line data={data} options={options} />

Is there a way to render same ChartJS component again and again in same page?

I am working with chartJS in react. I have a single chartJs graph component which I want to use more than one. But when page renders it only display first chart and second chart is not displayed.
Below is the Chart Component I want to reuse.
import React, { useEffect } from "react";
import Chart from "chart.js";
var waterFallChart;
export default function WaterFallChart(props) {
useEffect(() => {
if (waterFallChart) {
waterFallChart.destroy();
}
const ctx = document.getElementsByClassName(props.chartClass);
waterFallChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["sun","mon","tue","wed","thurs","fri"],
datasets: [
{
label: 'Good',
data: [10,20,30,40,50,60],
backgroundColor: "#0058FF",
},
]
},
options: {
maintainAspectRatio: false,
showAllTooltips: false,
scales: {
yAxes: [
{
ticks: {
fontColor: "silver",
fontSize: "12",
},
display: true,
gridLines: {
display: true,
zeroLineColor: "white",
zeroLineWidth: "1",
},
},
],
xAxes: [
{
ticks: {
fontColor: "silver",
fontSize: "10",
},
gridLines: {
display: true,
color: "rgb(255,255,255, 0.2)",
zeroLineColor: "white",
zeroLineWidth: "2",
},
},
],
},
legend: {
display: false,
},
},
});
});
return (
<React.Fragment>
<div className="canvasParentDiv">
<canvas className={props.chartClass} />
</div>
</React.Fragment>
);
}
And Main component which uses WaterFallChart component.
import React from 'react'
import WaterFallChart from './WaterFallChart'
function mainComponent() {
return (
<div>
<WaterFallChart chartClass="chart1" />
<WaterFallChart chartClass="chart2" />
<WaterFallChart chartClass="chart2"/>
</div>
)
}
export default mainComponent
Since we can't have same ID for the canvas, I am targeting canvas with className, Yet it just displays one chart and rest two remains hidden.

Chartjs-plugin: How to add a different color to each label?

I have a piechart made with chartjs. I then added a label on each pie thanks to chart-js plugin. Sometimes, the pie background is a bit light, so my white text is not visible enough. I've made a function that check the contrast and color it to black in such a case.
But when applied to chartjs-plugin's label color property, the function runs only once, and keep the same color for all label. How to apply a different color to each label based on its background?
Here is the code:
plugins: {
datalabels: {
color: function(ctx: any) {
[...Array(ctx.dataset.data.length)].map((_, i) => {
return transformColor(ctx.dataset.backgroundColor[i]);
});
},
Thanks!
EDIT:
here is the rendering of my current piechart:
you need to provide backgroundColor: [] in Pie Chart for background color. Here is the complete example:
import React from "react";
import {makeStyles} from "#material-ui/core/styles";
import {Pie} from "react-chartjs-2";
const useStyles = makeStyles(theme => ({
chart: {
marginLeft: theme.spacing(2)
}
}));
export default function PieChartExample(props) {
const classes = useStyles();
const [data, setdata] = React.useState({
labels: ["type1", "type2", "type3", "type4"],
datasets: [
{
label: "No. of registrations made",
backgroundColor: [
"#3e95cd",
"#8e5ea2",
"#3cba9f",
"#e8c3b9",
"#c45850"
],
barThickness: 80,
data: [50, 100, 75, 20, 0]
}
]
});
const getChartData = canvas => {
return data;
};
return (
<React.Fragment>
<div
className={classes.chart}
style={{position: "relative", width: 900, height: 450}}
>
<Pie
options={{
responsive: true,
maintainAspectRatio: true,
legend: {display: true},
title: {
display: true,
text: "Title for the graph"
}
}}
onElementsClick={(e) => { console.log(e, 'e')}}
data={getChartData}
/>
</div>
</React.Fragment>
);
}

Show/hide dynamic data on button click in vuejs

I want to dynamically show and hide the line chart on every button click, but when I click on any button, data is not changing. Every time same data is visible to me. Here is my code:
<template>
<div>
<v-btn color="primary" mr-2 #click="changeTab('week')">Week</v-btn>
<v-btn color="error" mr-2 #click="changeTab('month')">Month</v-btn>
<v-btn color="info" mr-2 #click="changeTab('year')">Year</v-btn>
<div v-if="selectedChartData !=null">
<line-chart
:width="650"
:height="400"
:dataSet= "selectedChartData.ChartData"
:labels= "selectedChartData.ChartLabel"
:chartColorsData="selectedChartData.ChartColors"
:label="selectedChartData.ChartData"
>
</line-chart>
</div>
</div>
</template>
<script>
import LineChart from "Components/Charts/LineChart";
import { buySellChartData } from 'Assets/data/buySellChartData.js'
export default {
components:{
LineChart,
},
data() {
return {
buySellChartData,
selectedButton: 'week',
selectedChartData: null,
};
},
mounted(){
this.selectedChart(this.selectedButton);
},
methods:{
selectedChart(selectedButton){
for(var i=0; i< this.buySellChartData.length; i++){
if(this.buySellChartData[i].tag == selectedButton) {
this.selectedChartData = this.buySellChartData[i];
break;
}
}
},
changeTab(selectedBtn){
this.selectedButton = selectedBtn;
this.selectedChart(this.selectedButton);
}
}
};
</script>
where I am assigning selected data to a variable "selectedChartData" on a button click and passing to a line chart component. In "this.buySellChartData[i].tag" tag is having value "year,week or month".
Here is the line chart code:
import { Line } from 'vue-chartjs'
const lineTension = 0.1;
const borderWidth = 3;
const pointRadius = 2;
const pointBorderWidth = 2;
export default {
extends: Line,
props: {
dataSet: {
type: Array
},
label: {
type: Array,
},
labels: {
type: Array
},
chartColorsData:{
type: Array
},
},
data() {
return {
options: {
scales: {
yAxes: [{
gridLines: {
display: true,
drawBorder: true
},
ticks: {
stepSize: 20,
// padding: 5
display:true
}
}],
xAxes: [{
gridLines: {
display: false,
drawBorder: false
},
ticks: {
// padding: 10
display:true
}
}]
},
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false
}
}
},
mounted() {
this.renderChart({
labels: this.labels,
datasets: [
{
label: (this.label[0]).label,
lineTension,
borderColor: this.chartColorsData[0].borderColor,
pointBorderColor: this.chartColorsData[0].borderColor,
pointBorderWidth,
pointRadius,
fill: false,
pointBackgroundColor: '#FFF',
borderWidth,
data: this.dataSet[0].data
},
{
label: this.label[1].label,
lineTension,
borderColor: this.chartColorsData[1].borderColor,
pointBorderColor: this.chartColorsData[1].borderColor,
pointBorderWidth,
pointRadius,
fill: false,
pointBackgroundColor: '#FFF',
borderWidth,
data: this.dataSet[1].data
},
{
label: this.label[2].label,
lineTension,
borderColor: this.chartColorsData[2].borderColor,
pointBorderColor: this.chartColorsData[2].borderColor,
pointBorderWidth,
pointRadius,
fill: false,
pointBackgroundColor: '#FFF',
borderWidth,
data: this.dataSet[2].data
},
]
}, this.options)
}
}
Please open the link to see the screenshot that what kind of chart I am creating https://www.awesomescreenshot.com/image/4110976/35de049e785364eec1006c23301dcf2f. So How it should be possible to show different chart on each button click. If someone needs more info, Please let me know. Any help will be appreciated. Thanks!
vue-chartjs does not provide a live update if you change the datasets. However, vue-chartjs provides two mixins to achieve this.
reactiveProp
reactiveData
so add reactiveProp mixin to you compenent with make a live update of chart on dataset change or update
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options'],
mounted () {
// this.chartData is created in the mixin.
// If you want to pass options please create a local options object
this.renderChart(this.chartData, this.options)
}
}

Categories

Resources