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

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>

Related

Function does not work on first render instead the series is generated after the subsequent renders

My function generateSeriesDataWithColor() seems like it does not load before the component or page renders.
So, the seriesWithColor should get the data genrated by generateSeriesDataWithColor() right away when the component is loaded but it does not get generated at the first render, instead if the component is rendered again, the colors and the graph shows up.
import HighchartsReact from "highcharts-react-official";
import Highcharts from "highcharts";
import './SkillsGraph.scss';
import { Col, Row } from "react-bootstrap";
import HeadingMain from "../../Heading/HeadingMain/HeadingMain";
export default function SkillsGraph(){
const skills = ['HTML5/CSS3/JS', 'Java11', 'PHP', 'MySql', 'MongoDB', 'ReactJS', 'ExpressJS'];
const series = {
name: 'Skill Level',
data: [ 10, 9.5, 7, 9.5, 8, 8.5, 8]
};
const seriesWithColor = generateSeriesDataWithColor(series); // This is where the series is assigned to the var
// Randomly generate colors
function generateRandomColor(){
let maxVal = 0xFFFFFF; // 16777215
let randomNumber = Math.random() * maxVal;
randomNumber = Math.floor(randomNumber);
randomNumber = randomNumber.toString(16);
let randColor = randomNumber.padStart(6, 0);
return `#${randColor.toUpperCase()}`
}
// Generate the data with random conlor
function generateSeriesDataWithColor(seriesData){
const data = seriesData.data;
const dataArray = data.map((item) => {
let color = generateRandomColor();
while(color === "#FFFFFF"){
color = generateRandomColor();
}
let dataObj = {
y: item,
color: color
}
return dataObj;
})
let seriesWithColor = {
name: 'Skill Level',
data: dataArray
}
return seriesWithColor; //This is from where the data/series is returned
}
// Options for the graph
let options = {
chart: {
type: 'bar',
height: 400
},
title: {
align: 'left',
text: 'Skills represented'
},
xAxis: {
categories: skills,
visible: true,
type: 'Skills categorised',
title: {
text: null
}
},
yAxis: {
min: 0,
max: 10,
title: {
text: 'Skill Level',
align: 'high'
},
labels: {
overflow: 'justify'
}
},
plotOptions: {
bar: {
dataLabels: {
enabled: false
}
},
column: {
colorByPoint: true
}
},
colors: [
'#ff0000',
'#00ff00',
'#0000ff',
'#0000ff',
'#0000ff',
'#0000ff',
'#0000ff'
],
legend: {
enabled: true
},
credits: {
enabled: false
},
series: seriesWithColor // This is where the generated data/series is used
}
return (
<Row>
<Col md={3}>
<HeadingMain name="This is Legend"></HeadingMain>
</Col>
<Col md={9}>
<HighchartsReact highcharts={Highcharts} options={options} className="chart"></HighchartsReact>
</Col>
</Row>
)
}
Does anyone have a solution for this?
I tried using useEffect hook to complete the wanted task but it gives an error message - 'React Hook useEffect has missing dependencies: 'generateSeriesDataWithColor' and 'series'. Either include them or remove the dependency array react-hooks/exhaustive-deps'. (Please check the code below)
const [seriesWithColor, setSeries] = useState(null);
useEffect(() => {
generateSeriesDataWithColor(series)
.then(data => setSeries(data))
.catch(err => console.log(err));
}, []);
Series needs to be an array of objects instead of a single object:
let options = {
...,
series: [seriesWithColor] // This is where the generated data/series is used
};
Live demo: https://codesandbox.io/s/highcharts-react-demo-h4r493?file=/demo.jsx
API Reference: https://api.highcharts.com/highcharts/series
You will need to call the function within useEffect hook to make sure that the data is available.

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.

Filter/update already rendered chart.js in react.js

I'm new here, because I have decided to dive into programming, so I can fill free time between treatments in the hospital. I'm absolutely new in the programming field with no previous coding background.
The summary:
I am working on a simple page, where I fetch data from a Postgre database that is visualized using chart.js. The page is a built-in cube.js playground, using a Reactjs template. Currently, I can display various charts depending on my criteria. Like display monthly sales of a certain product in Australia. Or, I can display a second chart with daily sales in the countries I choose. Or ignore all sales that were in a certain currency. Right now, every new criterion means I have to use cube.js playground and generate a new chart on the page.
What I would like to achieve is to be able to filter already rendered charts (by a dropdown button outside the chart or inside the chart, it doesn't matter too much) and having the chart updated. Something like the pictures here, where the OP can filter charts based on the date, factory, etc.
I've tried Chart.js Example with Dynamic Dataset, chart.js tutorial on
Updating Charts and various others. But I can't seem to be able to implement any of those solutions in my code.
Here is my current code:
ChartRenderer.js
import React from "react";
import PropTypes from "prop-types";
import { useCubeQuery } from "#cubejs-client/react";
import Row from "react-bootstrap/Row";
import Spin from "react-bootstrap/Spinner";
import Col from "react-bootstrap/Col";
import { Statistic, Table } from "antd";
import { Line, Bar, Pie } from "react-chartjs-2";
const COLORS_SERIES = [
"#931F1D",
"#141446",
"#7A77FF",
];
const commonOptions = {
maintainAspectRatio: true,
};
const TypeToChartComponent = {
line: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
borderColor: COLORS_SERIES[index],
backgroundColor: COLORS_SERIES[index],
fill: false,
tension: 0.4,
})),
};
const options = { ...commonOptions };
return <Line data={data} options={options} />;
},
bar: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES[index],
fill: false,
})),
};
const options = {
...commonOptions,
scales: {
xAxes: [
{
stacked: true,
},
],
},
};
return <Bar data={data} options={options} />;
},
area: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s, index) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES[index],
fill: true,
})),
};
const options = {
...commonOptions,
scales: {
yAxes: [
{
stacked: true,
},
],
},
};
return <Line data={data} options={options} />;
},
pie: ({ resultSet }) => {
const data = {
labels: resultSet.categories().map((c) => c.category),
datasets: resultSet.series().map((s) => ({
label: s.title,
data: s.series.map((r) => r.value),
backgroundColor: COLORS_SERIES,
hoverBackgroundColor: COLORS_SERIES,
borderColor: COLORS_SERIES,
hoverBorderColor: "white",
hoverOffset: 10,
})),
};
const options = { ...commonOptions };
return <Pie data={data} options={options} />;
},
number: ({ resultSet }) => {
return (
<Row
type="flex"
justify="space-around"
align="middle"
style={{ height: "100%" }}
>
<Col align="left">
{resultSet.seriesNames().map((s) => (
<Statistic value={resultSet.totalRow()[s.key]} />
))}
</Col>
</Row>
);
},
table: ({ resultSet, pivotConfig }) => {
return (
<Table
pagination={false}
columns={resultSet.tableColumns(pivotConfig)}
dataSource={resultSet.tablePivot(pivotConfig)}
/>
);
},
};
const TypeToMemoChartComponent = Object.keys(TypeToChartComponent)
.map((key) => ({
[key]: React.memo(TypeToChartComponent[key]),
}))
.reduce((a, b) => ({ ...a, ...b }));
const renderChart =
(Component) =>
({ resultSet, error }) =>
(resultSet && <Component resultSet={resultSet} />) ||
(error && error.toString()) || <Spin animation="grow text-primary" />;
const ChartRenderer = ({ vizState }) => {
const { query, chartType } = vizState;
const component = TypeToMemoChartComponent[chartType];
const renderProps = useCubeQuery(query);
return component && renderChart(component)(renderProps);
};
ChartRenderer.propTypes = {
vizState: PropTypes.object,
cubejsApi: PropTypes.object,
};
ChartRenderer.defaultProps = {
vizState: {},
cubejsApi: null,
};
export default ChartRenderer;
DashBoardPage.js
import React from "react";
import Col from "react-bootstrap/Col";
import DateRangePicker from 'react-bootstrap-daterangepicker';
import ChartRenderer from "../components/ChartRenderer";
import Dashboard from "../components/Dashboard";
import DashboardItem from "../components/DashboardItem";
const DashboardItems = [
{
id: 0,
name: "Sold by customers today",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
granularity: "day",
dateRange: "Today",
},
],
order: {},
dimensions: [],
filters: [
{
member: "PostgreSqlTable.operation",
operator: "contains",
values: ["Sell"],
},
],
},
chartType: "number",
},
},
{
id: 1,
name: "Bought by customers today",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
dateRange: "Today",
},
],
order: {},
filters: [
{
member: "PostgreSqlTable.operation",
operator: "contains",
values: ["Buy"],
},
],
},
chartType: "number",
},
},
{
id: 2,
name: "Money in the wallet",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
},
],
order: {
"PostgreSqlTable.amount": "desc",
},
dimensions: ["PostgreSqlTable.currency"],
filters: [
{
member: "PostgreSqlTable.currency",
operator: "equals",
values: ["EUR"],
},
],
},
chartType: "number",
},
},
{
id: 3,
name: "Monthly sales filtered by week",
vizState: {
query: {
measures: ["PostgreSqlTable.amount"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
granularity: "week",
dateRange: "This month",
},
],
order: {
"PostgreSqlTable.amount": "desc",
},
dimensions: ["PostgreSqlTable.operation"],
filters: [
{
member: "PostgreSqlTable.operation",
operator: "notContains",
values: ["Register"],
},
],
limit: 5000,
},
chartType: "line",
},
},
{
id: 4,
name: "Countries with most customers",
vizState: {
query: {
measures: ["PostgreSqlTable.count"],
timeDimensions: [
{
dimension: "PostgreSqlTable.added",
},
],
order: {
"PostgreSqlTable.count": "desc",
},
dimensions: ["PostgreSqlTable.country"],
limit: 5,
},
chartType: "pie",
},
},
];
const DashboardPage = () => {
const dashboardItem = (item) => (
<Col className="col-4">
<DashboardItem title={item.name}>
<ChartRenderer vizState={item.vizState} />
</DashboardItem>
</Col>
);
const Empty = () => (
<div
style={{
textAlign: "center",
padding: 12,
}}
>
<h2>
No items added
</h2>
</div>
);
return DashboardItems.length ? (
<Dashboard dashboardItems={DashboardItems}>
{DashboardItems.map(dashboardItem)}
</Dashboard>
) : (
<Empty />
);
};
export default DashboardPage;
At this moment, I have no clue how to implement the filter in react.js+chart.js. I have also tried to update the array, but no success (I followed also this tutorial)
I would be most grateful for any help.
Thank you in advance, stay healthy.
Tatsu
I'd recommend using the <QueryBuilder/> component available in the Cube.js-React integration; this component provides a similar interface as that in the Developer Playground.

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

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]}});
}

Resources