(Lifecycle problem?) React Chart js-2 not updating data from componentDidMount - reactjs

I am trying to populate chart data from my backend.
Although I am fetching data and pushing data in componentDidMount, the Bars or Scatters are not loaded on page load.
If I change my screen width in inspect mode in google dev tools, it starts loading which leads me to believe this is a lifecyle problem.
However, changing it to componentWillMount did not change anything. Putting a if statement before render like below just stops loading the chart altogether.
if(this.state.data.datasets[0].data.length ===0){
return null;
}
Any way to fix this problem?
import React, { Component } from "react";
import axios from "axios";
import { Bar, Scatter } from "react-chartjs-2";
export class Data extends Component {
constructor(props) {
super(props);
this.state = {
provider: [],
data: {
labels: ["Action", "Anime", "Children"],
datasets: [
{
label: "Total",
backgroundColor: "rgba(255, 159, 64, 0.4)",
borderColor: "white",
borderWidth: 1,
stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: []
},
{
label: "Above ⭐️8.5",
backgroundColor: "white",
type: "scatter",
showLine: false,
stack: 1,
data: []
}
]
}
};
}
componentDidMount() {
axios.get("http://localhost:8001/provider").then(res =>
this.setState({ provider: res.data }, () => {
this.pushAction();
})
);
}
pushAction() {
const dataState = this.state.data;
const oldDataTotal = this.state.data.datasets[0].data;
const oldDataGood = this.state.data.datasets[1].data;
oldDataTotal.push(this.state.provider[0].huluAction);
oldDataTotal.push(this.state.provider[0].huluAnime);
oldDataTotal.push(this.state.provider[0].huluChildren);
oldDataGood.push(this.state.provider[0].Action);
oldDataGood.push(this.state.provider[0].Anime);
oldDataGood.push(this.state.provider[0].Children);
}
render() {
console.log(this.state.musicalData);
const options = {
responsive: true,
maintainAspectRatio: false,
legend: {
display: true
},
type: "bar",
scales: {
xAxes: [
{
stacked: true
}
],
yAxes: [
{
stacked: true
}
]
}
};
return (
<div>
<Bar data={this.state.data} height={300} options={options} />
</div>
);
}
}
export default Data;

you have to update the state after changing the datasets,
pushAction = () => {
const dataState = this.state.data;
const oldDataTotal = this.state.data.datasets[0].data;
const oldDataGood = this.state.data.datasets[1].data;
oldDataTotal.push(this.state.provider[0].huluAction);
oldDataTotal.push(this.state.provider[0].huluAnime);
oldDataTotal.push(this.state.provider[0].huluChildren);
oldDataGood.push(this.state.provider[0].Action);
oldDataGood.push(this.state.provider[0].Anime);
oldDataGood.push(this.state.provider[0].Children);
this.setState({data: {...dataState, datasets : [...oldDataTotal, oldDataGood]}});
}

Related

update options without reseting the zoom

I am using React ApexChart in order to show off some data, and I have a switch that shows and hides the data labels. The options are changing without problems, but the zoom level is resetting every time. Is there a way to keep the zoom level when changing the options?
Here is a small preview of the component:
import Chart from "react-apexcharts";
import React, { useEffect, useState } from 'react';
import { Layout } from "antd";
interface Props {
series?: ApexAxisChartSeries;
chartOptiosn?: Record<string, any>;
[ key: string ]: any;
}
export const DynamicChart: React.FC<Props> = React.memo( ( props: Props ) => {
const { Content } = Layout;
const { series, chartOptions, uuid } = props;
const [ initialRerender, setInitialRerender ] = useState( false );
useEffect( () => {
if ( !initialRerender ) {
setInitialRerender( true );
}
}, [ initialRerender ] );
useEffect( () => {
}, [] );
useEffect( () => {
console.log( chartOptions );
}, [ chartOptions ] );
return (
<Layout>
<Content>
<Chart
options={ { ...structuredClone( chartOptions || {} ), chart: { ...chartOptions.chart, id: uuid } } }
series={ series }
type="area"
height={ 350 }
/>
</Content>
</Layout>
);
} );
Here are the options:
export const defaultOptions: ApexCharts.ApexOptions = {
series: [],
chart: {
animations: {
enabled: true,
easing: "linear",
dynamicAnimation: {
speed: 300
}
},
toolbar: {
show: true,
tools: {
download: `<svg viewBox="64 64 896 896" focusable: "false" name= "download" theme="outlined"><path d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg>`,
selection: false,
zoom: true,
zoomin: true,
zoomout: true,
pan: true,
customIcons: []
},
},
zoom: {
enabled: true
}
},
dataLabels: {
enabled: false
},
yaxis: {
labels: {
style: {
fontSize: "14px",
fontWeight: 600,
colors: [ "#8c8c8c" ],
},
},
},
xaxis: {
type: "datetime"
}
};
Here is a demo for more depth of the problem that I am facing.
Just zoom in between the 3 seconds interval and after the labels reappear, the zoom is refreshed. Is there a way to stop that in react, or should I seek a vanilla approach and ditch the react solution all together?

Can't perform a React state update on an unmounted component, using class component and component did mount

I have certain code as below:-
class BarChart extends Component {
constructor(){
super();
this.state = {
chartData:{}
}
}
componentDidMount() {
this.getChartData();
}
getChartData() {
axios.get("http://localhost:5001/inventory/clusterscount").then(res => {
const myresponse = res.data;
console.log(myresponse)
let countcluster = [];
let listregion = [];
for (const dataobj of myresponse){
countcluster.push(parseInt(dataobj.clusterscount));
listregion.push(dataobj.region);
}
console.log(countcluster)
console.log(listregion)
this.setState({
chartData: {
labels:listregion,
datasets: [
{
label: "level of thiccness",
data: countcluster,
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
}
});
});
}
render(){
return (
<div className="App">
<h1>Dankmemes</h1>
<div>
<Line
data={this.state.chartData}
options={{
responsive: true,
title: { text: "THICCNESS SCALE", display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 10,
beginAtZero: true
},
gridLines: {
display: false
}
}
],
xAxes: [
{
gridLines: {
display: false
}
}
]
}
}}
/>
</div>
</div>
);
}
}
export default BarChart;
Now while running it am getting the desired clusters and regions as below:-
0: {clusterscount: '2', region: 'test1'}
1: {clusterscount: '10', region: 'test2'}
2: {clusterscount: '8', region: 'test3'}
3: {clusterscount: '1', region: 'test4'}
4: {clusterscount: '8', region: 'test5'}
5: {clusterscount: '2', region: 'test6'}
I am able to get results for clustercount and listregion as well, but keep getting this error. I have tried multiple things but out of ideas.
But in logs am getting as below:-
Can someone help me with this?
The react useEffect expects a cleanup function to cancel subscription and asynchronus tasks so we need to check if component is mounted or not there are couple of ways we can do it and react community have good solution for that.
const LineChart = () =>{
const [chartData,setChartData]= useState({});
const [myresponse, setmyresponse] =useState([]);
const isMountedRef = useRef(null);
useEffect(() =>{
isMountedRef.current = true;
let countcluster = [];
let listregion = [];
axios.get("http://localhost:5001/inventory/clusterscount").
then(res=>{
if(isMountedRef.current){
const myresponse= res.data;
setmyresponse(myresponse)
console.log(myresponse);
for (const dataobj of myresponse){
countcluster.push(parseInt(dataobj.clusterscount));
listregion.push(dataobj.region);
}
setChartData({
labels: listregion,
datasets: [
{
label: "level of thiccness",
data: countcluster,
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
});
}
})
.catch(err=>{
console.log(err);
});
return () => isMountedRef.current = false;
},[])

Reverse the order of data from an API call works with console.log

Building a Covid tracker using React and ChartJS-2 I found that I couldn't reverse the incoming data through usual means ie. through the recommendations in the chartJS docs. What seems to work is console.log(res.data.data.reverse())
Without this line of code printing to the console, the data displays the opposite way around.
Why is this?
The xAxes params don't take effect, either, but I'm probably just missing a '}' or ']', somewhere.
import { useState, useEffect } from 'react';
import { Line } from 'react-chartjs-2';
import axios from 'axios';
const CovidCases = () => {
const [chartData, setChartData] = useState({});
const chart = () => {
//set variables to pass in as dynamic data
let covCase = [];
let covDate = [];
// axios get endpoint
axios
.get(
'https://api.coronavirus.data.gov.uk/v1/data?filters=areaName=England;areaType=nation&structure={"date":"date","name":"areaName","code":"areaCode","newCasesByPublishDate":"newCasesByPublishDate","cumCasesByPublishDate":"cumCasesByPublishDate","newDeaths28DaysByPublishDate":"newDeaths28DaysByPublishDate","cumDeaths28DaysByPublishDate":"cumDeaths28DaysByPublishDate"}',
)
.then((res) => {
// here I reverse the order that the data comes into the console
console.log(res.data.data.reverse());
for (const dataObj of res.data.data) {
covCase.push(parseInt(dataObj.newCasesByPublishDate));
covDate.push(parseInt(dataObj.date));
// setting the chart data STATE with the chart.js labels and datasets with data: covCase variable
setChartData({
// https://www.chartjs.org/docs/latest/getting-started/usage.html for layout requirements
labels: covDate,
datasets: [
{
label: 'New Covid Cases by Date (England)',
data: covCase,
backgroundColor: ['rgba(81, 250, 157, 0.6)'],
borderWidth: 2,
},
],
});
}
})
.catch((err) => {
console.log(err);
}).catch((err) => {
console.log(err.message)
})
// console.log(covCase, covDate);
};
useEffect(() => {
chart();
}, []);
return (
<div className="charts" style={{ height: 700, width: 900 }} >
<Line
// passing in the STATE as the data to be rendered
data={chartData}
// chart.js options parameters
options={{
title: { text: 'Covid Cases', display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
beginAtZero: true,
},
},
],
xAxes: [
{
autoSkip: true,
padding: 10,
// this section didn't make the difference expected of it. reversing the console.log() did, though!!
// ticks: {
// // reverse: true,
// maxTicksLimit: 7,
// display: false,
// },
},
],
},
}}
/>
</div>
);
};
export default CovidCases;
Also, the xAxes parameters were taking no effect but I think I'm within the code recommendations?
Thanks.

How to customize border style on React Chart.js 2

I am trying to add dashed border to my bar charts. I am following this example here- https://jsfiddle.net/jt100/ghksq1xv/3/
I am not getting much luck I have followed the instruction very carefully and passing in the correct values but I am not adding the dashed border to my bar chart. Any help will be very much appreciated
This is what I have done
1) passed in 4 arguments: my chart instance, dataset, data and dash
```
this.dashedBorder(myLineChart, 0, 2, [15, 10]);
2) This is my function.
dashedBorder(chart, dataset, data, dash) {
chart.config.data.datasets[dataset]._meta[0].data[data].draw = function() {
chart.chart.ctx.setLineDash(dash);
Chart.elements.Rectangle.prototype.draw.apply(this,
arguments,
);
};
}
3) my whole react component. You can see what I have done here.
import React, { PureComponent } from "react";
import classes from "./YourLineGraph.module.css";
import Chart from "chart.js";
let myLineChart;
let myChartRef;
let ctx;
//--Chart Style Options--//
// Chart.defaults.global.defaultFontFamily = "'PT Sans', sans-serif";
Chart.defaults.global.defaultFontFamily = "'Cooper Hewitt'";
Chart.defaults.global.legend.display = false;
Chart.defaults.global.elements.line.tension = 0;
Chart.defaults.global.scaleLineColor = "tranparent";
Chart.defaults.global.tooltipenabled = false;
//--Chart Style Options--//
export default class YourLineGraph extends PureComponent {
chartRef = React.createRef();
componentDidMount() {
this.buildChart();
}
componentDidUpdate() {
this.buildChart();
}
buildChart = () => {
myChartRef = this.chartRef.current.getContext("2d");
ctx = document.getElementById("myChart").getContext("2d");
const { data, average, labels, attribute } = this.props;
if (typeof myLineChart !== "undefined") myLineChart.destroy();
myLineChart = new Chart(myChartRef, {
type: "bar",
data: {
//Bring in data
labels:
labels.length === data.length
? labels
: new Array(data.length).fill("Data"),
datasets: [
{
label: "Sales",
data: data,
borderColor: "#98B9AB",
borderWidth: 3,
borderStyle: "dash" //has no effect
}
]
},
options: {
plugins: {
datalabels: {
formatter: function(value, context) {
return attribute === "pounds" ? `£ ${value}` : value;
},
anchor: "end",
align: "end",
color: "#888"
}
},
scales: {
yAxes: [
{
gridLines: {
drawBorder: false,
display: false
},
ticks: {
display: false //this will remove only the label
}
}
],
xAxes: [
{
gridLines: {
drawBorder: false,
display: false
},
ticks: {
display: false //this will remove only the label
}
}
]
}
}
});
this.dashedBorder(myLineChart, 0, 2, [15, 10]);
};
dashedBorder(chart, dataset, data, dash) {
chart.config.data.datasets[dataset]._meta[0].data[data].draw = function() {
chart.chart.ctx.setLineDash(dash);
Chart.elements.Rectangle.prototype.draw.apply(this, arguments);
chart.chart.ctx.setLineDash([1, 0]);
};
}
render() {
return (
<div className={classes.graphContainer}>
<canvas id="myChart" ref={this.chartRef} />
</div>
);
}
}

How to get data from db using json format inside componentDidMount()

I have a set of data in mongodb in Json format, I want to get that out and populate it in react js for my pie chart, can you please help me how to do that. Below the code I have. In this I get the data from the DB using fetch and store it in ddts from this.state, but I am not able to get all the data from that ddts and populate it in the data list for the pie chart. I have the data in json format in the db as
{"success":true,"data":[{"_id":"5d33f8bdd7f819c52b4406aa","total":"51","resolved":"20","assigned":"15","open":"0","new":"1","info":"2","closed":"0","unrepro":"6","duped":"3","junk":"0"}]}
Below is the react.js frontend code. Can anyone please help me populate the date to display in the pie chart.
componentDidMount() {
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 1000);
this.setState({intervalIsSet: interval});
}
}
getDataFromDb = () => {fetch('http://172.24.78.202:3002/api/ddts')
.then(data => data.json())
.then(res => this.setState({ ddts: res.data }));
};
componentDidMount() {
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 1000);
this.setState({ intervalIsSet: interval });
}
const { ddts } = this.state;
const { resolved } = ddts.map((dat) => {dat.resolved})
const chartConfig = {
type: "pie",
data: {
datasets: [
{
hoverBorderColor: "#ffffff",
data: [{resolved}, 24, 7.5, 4, 5, 6, 7],
backgroundColor: [
"rgba(0, 146, 0, 1.1)",
"rgba(0,123,255,0.5)",
"rgba(0,123,255,0.1)",
"rgba(0,123,255,0.1)",
"rgba(0,123,255,0.1)",
"rgba(0,123,255,0.1)",
"rgba(0,123,255,0.1)"
]
}
],
labels: ["Resolved", "Assigned", "New", "Closed", "Junk",
"Duped", "Unreproduciable"]
},
options: {
...{
legend: {
position: "bottom",
labels: {
padding: 25,
boxWidth: 20
}
},
cutoutPercentage: 0,
tooltips: {
custom: false,
mode: "index",
position: "nearest"
}
},
...this.props.chartOptions
}
};
new Chart(this.canvasRef.current, chartConfig);
}
render() {
const { ddts } = this.state;
const { title } = this.props;
return (
<Card small className="h-100">
<CardHeader className="border-bottom">
<h6 className="m-0">{title}</h6>
</CardHeader>
<CardBody className="d-flex py-0">
<canvas
height="220"
ref={this.canvasRef}
className="blog-users-by-device m-auto"
/>
</CardBody>

Resources