Issue rendering react charts - reactjs

I am dynamically changing the values that are displayed on the areachart. But for some reason the chart is only displayed if I change one of the dynamic variable with a hard coded number in the array. For example
const data = [
["Year", "Free Cash Flow", "Revenue"],
[this.props.date1, this.props.cashFlow1, this.props.revenue1],
[this.props.date2, this.props.cashFlow2, this.props.revenue2],
[this.props.date3, this.props.cashFlow3, this.props.revenue3],
[this.props.date4, this.props.cashFlow4, this.props.revenue4],
[this.props.date5, this.props.cashFlow5, this.props.revenue5],
];
that is how I structured my data array, but it doesn't renders and give the following error All series on a given axis must be of the same data type. However, if I replace this.props.revenue1 with let say 100 the area chart renders
const data = [
["Year", "Free Cash Flow", "Revenue"],
[this.props.date1, this.props.cashFlow1, 100],
[this.props.date2, this.props.cashFlow2, this.props.revenue2],
[this.props.date3, this.props.cashFlow3, this.props.revenue3],
[this.props.date4, this.props.cashFlow4, this.props.revenue4],
[this.props.date5, this.props.cashFlow5, this.props.revenue5],
];
I have looked at other examples and I can't seem to find a mistake I could've made.
import React, {Component} from "react";
import { Chart } from "react-google-charts";
class AreaChart extends Component {
render () {
const chartEvents = [
{
callback: ({ chartWrapper, google }) => {
const chart = chartWrapper.getChart();
chart.container.addEventListener("click", (ev) => console.log(ev))
},
eventName: "ready"
}
];
const rev1 = this.props.revenue1;
const FCF1 = this.props.cashFlow1;
const data = [
["Year", "Free Cash Flow", "Revenue"],
[this.props.date1, this.props.cashFlow1, this.props.revenue1],
[this.props.date2, this.props.cashFlow2, this.props.revenue2],
[this.props.date3, this.props.cashFlow3, this.props.revenue3],
[this.props.date4, this.props.cashFlow4, this.props.revenue4],
[this.props.date5, this.props.cashFlow5, this.props.revenue5],
];
const options = {
isStacked: true,
height: 300,
legend: { position: "top", maxLines: 3 },
vAxis: { minValue: 0 },
};
return (
<Chart
chartType="AreaChart"
width="75%"
height="400px"
data={data}
options={options}
chartEvents={chartEvents}
/>
);
}
}
export default AreaChart;

Related

Problems while importing react-icon-cloud

when im trying to import the code via sandbox or trying to modify the github repo code its giving me (NextJS)
im not used to typescript btw and when im directly importing code from codesandbox its showing me no tags since its not dynamically imported as required in NEXTJS
heres the error
'Cannot read properties of undefined (reading 'forEach')' error
heres the code btw ( og github link for reference : https://github.com/teaguestockwell/react-icon-cloud )
import allIcons from "simple-icons";
import { v4 } from "uuid";
import {
Cloud,
renderSimpleIcon,
fetchSimpleIcons,
SimpleIcon,
} from "react-icon-cloud";
const Componente = () => {
const tagCanvasOptions = {
clickToFront: 500,
// decel: number
depth: 1,
imageScale: 2,
initial: [0.1, -0.1],
outlineColour: "#0000",
reverse: true,
tooltip: "native", // null | 'div'
// tooltipClass: string
tooltipDelay: 0,
wheelZoom: false,
};
const slugs = [
"typescript",
"javascript",
"dart",
"java",
"react",
"flutter",
"android",
"html5",
"css3",
"nodedotjs",
"express",
"nextdotjs",
"prisma",
"amazonaws",
"postgresql",
"firebase",
"nginx",
"vercel",
"testinglibrary",
"jest",
"cypress",
"docker",
"git",
"jira",
"github",
"gitlab",
"visualstudiocode",
"androidstudio",
"sonarqube",
"figma",
];
const useIcons = (slugs) => {
const [icons, setIcons] = React.useState();
React.useEffect(() => {
fetchSimpleIcons(slugs).then(setIcons);
}, []);
if (icons) {
return Object.values(icons.simpleIcons).map((icon) =>
renderSimpleIcon({
icon,
size: 42,
aProps: {
onClick: (e) => e.preventDefault(),
},
})
);
}
return <a>Loading</a>;
};
const icons = useIcons(slugs);
return (
<div>
<Cloud>{icons}</Cloud>
</div>
);
};
export default Componente;

React ChartJS - difficulty manipulating data to a certain format

I have data stored in MongoDB that looks like this: (Example of 1 document below)
{_id: XXX,
project: 'Project ABC',
amount: 300,
expenseIncurredDate: 2022-03-15T08:24:46.000+00:00
}
I am trying to display my data in a stacked bar chart where the x-axis is year-month, and the y-axis is amount. Within each year-month, there can be several expenses incurred by different projects.
I am trying to achieve something like this, except that x-axis should show year-month (e.g. Jan 2021). Each project should be represented by one color for every year-month.
I am using Chart.js. Below is my Bar component.
import { Bar } from "react-chartjs-2";
import { Chart as ChartJS } from "chart.js/auto";
import React from "react";
const BarChart = ({ chartData }) => {
const options = {
responsive: true,
legend: {
display: false,
},
type: "bar",
scales: {
xAxes: [
{
stacked: true,
},
],
yAxes: [
{
stacked: true,
},
],
},
};
return <Bar data={chartData} options={options} />;
};
export default BarChart;
I am manipulating my data in another component:
let datasets = [];
function random_bg_color() {
var x = Math.floor(Math.random() * 256);
var y = 100 + Math.floor(Math.random() * 256);
var z = 50 + Math.floor(Math.random() * 256);
var bgColor = "rgb(" + x + "," + y + "," + z + ")";
return bgColor;
}
let amounts = myExpenses.map((expense) => expense.amount);
myExpenses.forEach((expense) => {
const expenseObj = {
label: expense.project,
stack: 1,
borderWidth: 1,
backgroundColor: random_bg_color(),
data: amounts,
};
datasets.push(expenseObj);
});
let data = {
labels: myExpenses.map((expense) =>
moment(expense.expenseDate, "DD-MM-YYYY")
),
datasets: datasets,
};
I know this doesn't work as it isn't returning amounts in the correct order/grouped by year & month.
let amounts = myExpenses.map((expense) => expense.amount);
I am having difficulty trying to group the amount by year month. Would appreciate any help on this, thank you.

React hooks async useEffect to load database data

I'm using async useEffect in React because I need to do database requests. Then, add this data to my react-charts-2
const [ plSnapShot, setPlSnapShot ] = useState({
grossIncome: 0.00,
opeExpenses: 0.00,
nonOpeExpenses: 0.00,
netIncome: 0.00,
grossPotencialRent: 0.00,
lastMonthIncome: 0.00
});
const [ thisMonthPayment, setThisMonthPayments ] = useState({
labels: [],
data: [],
color: 'blue'
});
useEffect(() => {
async function fetchData() {
await axios.get(`${url.REQ_URL}/home/getUserFullName/${userID}`)
.then(async (res) => {
setUserFullName(res.data);
await axios.get(`${url.REQ_URL}/home/getThisMonthPayments/${propertyID}`)
.then(async (resMonthPay) => {
let total = 0;
let obj = {
labels: [],
data: [],
color: 'blue'
};
const data = resMonthPay.data;
for(const d of data) {
obj.labels.push(helper.formatDate(new Date(d.date)));
obj.data.push(d.amount);
total += d.amount;
}
setThisMonthPayments(obj);
setTotalEarnedMonth(parseFloat(total));
await axios.get(`${url.REQ_URL}/home/plSnapshot/${propertyID}`)
.then(async (resPL) => {
const data = resPL.data;
setPlSnapShot({
grossIncome: parseFloat(data.GrossIncome || 0).toFixed(2),
opeExpenses: parseFloat(data.OperatingExpenses || 0).toFixed(2),
nonOpeExpenses: parseFloat(data.NonOperatingExpenses || 0).toFixed(2),
netIncome: parseFloat(data.NetIncome || 0).toFixed(2),
grossPotencialRent: parseFloat(data.GrossPotencialRent || 0).toFixed(2),
lastMonthIncome: parseFloat(data.LastMonthIncome || 0).toFixed(2)
});
});
});
});
}
fetchData();
}, [propertyID, userID]);
const pieChart = {
chartData: {
labels: ['Gross Income', 'Operating Expenses', 'Non Operating Expenses'],
datasets: [{
data: [plSnapShot.grossIncome, plSnapShot.opeExpenses, plSnapShot.nonOpeExpenses],
backgroundColor: [
ChartConfig.color.primary,
ChartConfig.color.warning,
ChartConfig.color.info
],
hoverBackgroundColor: [
ChartConfig.color.primary,
ChartConfig.color.warning,
ChartConfig.color.info
]
}]
}
};
const horizontalChart = {
label: 'Last Month Income',
labels: ['Gross Potencial Rent', 'Last Month Income'],
chartdata: [plSnapShot.grossPotencialRent, plSnapShot.lastMonthIncome]
};
Here is an example of how I call the Chart component in my code in the render/return method.
<TinyPieChart
labels={pieChart.chartData.labels}
datasets={pieChart.chartData.datasets}
height={110}
width={100}
/>
And my Pie Chart component is just to display it
import React from 'react';
import { Pie } from 'react-chartjs-2';
// chart congig
import ChartConfig from '../../Constants/chart-config';
const options = {
legend: {
display: false,
labels: {
fontColor: ChartConfig.legendFontColor
}
}
};
const TinyPieChart = ({ labels, datasets, width, height }) => {
const data = {
labels,
datasets
};
return (
<Pie height={height} width={width} data={data} options={options} />
);
}
export default TinyPieChart;
Mostly of the times it works just fine, but sometimes the chart data is loaded and displayed in the screen real quick, then it disappear and the chart is displayed empty (no data). Am I loading it properly with the useEffect or should I use another method?
Thanks you.
The momentary flashing is likely due to the fact that the chart data is empty on first render. So depending on the time it take for your useEffect to fetch the data, that flashing may present a real problem.
One common solution is to use a state variable to indicate that the data is being loaded and either not display anything in place of the chart or display a loaded of some sort. So you can add something like you suggested in the comments const [ loader, setLoader ] = useState(true). Then once the data is loaded, togged it to false.
Meanwhile, inside your render function, you would do:
...
...
{loader ?
<div>Loading....</div>
:
<TinyPieChart
labels={pieChart.chartData.labels}
datasets={pieChart.chartData.datasets}
height={110}
width={100}
/>
}
loader can go from true to false or vise versa, depending on what make more intuitive sense to you.

React Highcharts firing setExtremes event multiple times

I am using Highcharts React wrapper in an app using Hooks, when my chart is either loaded or zoomed it fires both setExtremes and setAfterExtremes multiple times each. I've looked through for similar questions but they are related to different issues.
I've reduced the code to the minimum setup, the page is not refreshing, the data is only parsed once and added to the chart once yet, animation is disabled and it's still consistently firing both events 7 times on:
* initial population
* on zoom
Versions: react 16.9, highcharts 7.2, highcharts-react-official 2.2.2
Chart
<HighchartsReact
ref={chart1}
allowChartUpdate
highcharts={Highcharts}
options={OPTIONS1}
/>
Chart Options:
const [series1, setSeries1] = useState([]);
const OPTIONS1 = {
chart: {
type: 'spline',
zoomType: 'x',
animation: false
},
title: {
text: ''
},
xAxis: {
events: {
setExtremes: () => {
console.log('EVENT setExtremes');
},
afterSetExtremes: () => {
console.log('EVENT sfterSetExtremes');
}
},
},
plotOptions: {
series: {
animation: false
}
},
series: series1
};
Data Population:
useEffect(() => {
if (data1) {
const a = [];
_.each(data1.labels, (sLabel) => {
a.push({
name: sLabel,
data: [],
})
});
... POPULATES DATA ARRAYS...
setSeries1(a);
}
}, [data1]);
Rather the question is old I also faced the same situation. The solution is to move the chart options to a state variable. Then the event will not fire multiple times.
It is mentioned on the library docs. https://github.com/highcharts/highcharts-react -- see the "optimal way to update"
import { render } from 'react-dom';
import HighchartsReact from 'highcharts-react-official';
import Highcharts from 'highcharts';
const LineChart = () => {
const [hoverData, setHoverData] = useState(null);
// options in the state
const [chartOptions, setChartOptions] = useState({
xAxis: {
categories: ['A', 'B', 'C'],
events: {
afterSetExtremes: afterSetExtremes,
},
},
series: [
{ data: [1, 2, 3] }
],
});
function afterSetExtremes(e: Highcharts.AxisSetExtremesEventObject) {
handleDateRangeChange(e);
}
return (
<div>
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
/>
</div>
)
}
render(<LineChart />, document.getElementById('root'));```
Your useEffect is getting fired multiple times probably because you are checking for data1 and data1 is changing. have you tried putting an empty array in your useEffect and see if it is firing multiple times?
if it only fires up once then the problem is that your useEffect is checking for a value that is constantly changing
if it still fires multiple times then there is something that is triggering your useEffect
I struggled the same problem after I SetState in useEffect().
My problem was I did a (lodash) deepcopy of the Whole options.
This also create a new Event every time.
// Create options with afterSetExtremes() event
const optionsStart: Highcharts.Options = {
...
xAxis: {
events: {
afterSetExtremes: afterSetExtremesFunc,
}
},
....
// Save in state
const [chartOptions, setChartOptions] = useState(optionsStart);
// On Prop Change I update Series
// This update deepcopy whole Options. This adds one Event Every time
React.useEffect(() => {
var optionsDeepCopy = _.cloneDeep(chartOptions);
optionsDeepCopy.series?.push({
// ... Add series data
});
setChartOptions(optionsDeepCopy);
}, [xxx]);
The fix is to Only update the Series. Not whole Options.
React.useEffect(() => {
var optionsDeepCopy = _.cloneDeep(chartOptions);
optionsDeepCopy.series?.push({
// ... Add series data
});
const optionsSeries: Highcharts.Options = { series: []};
optionsSeries.series = optionsDeepCopy.series;
setChartOptions(optionsSeries);
}, [xxx]);

React useEffect doesn't change data displayed on map

I'm playing with uber's react-map-gl and deck-gl libraries to try to display data dynamically.I've got 2 components in my react little app.A navigation bar and a Mapbox map.So here is my pipeline.When the page first loads,only the map is displayed without any marker or visual data.But when i click on one of navigation bar link,an action creator gets called,when i make an ajax call to fetch some data,and from the action that is dispatched a pass my response data as payload and the reducer is reached so that i have a new version of the store.the data that i would like to visualize in they stor key : mainProjects that contains an array of geojson.My click on a navbar link succefully updates that value,and in the actual Map component,i can load the new values but useEffect does not update my localstate.Here is my map code:
import React, { useState, useEffect, useContext } from "react";
import { StaticMap } from "react-map-gl";
import { MapContext } from "./contexts/MapProvider";
import DeckGL, { GeoJsonLayer } from "deck.gl";
import chroma from "chroma-js";
import { connect } from "react-redux";
const MAPBOX_TOKEN =
"pk.mykey";
const mapStyle = "mapbox://mymapstyle";
function getEnergyFillColor(regime) {
const ENERGY_COLORS = {
naturalGaz: "#FF8500",
coal: "#99979A",
nuclear: "#E0399E",
hydroelectric: "#0082CB",
Wind: "#00B53B",
solar: "#DBCA00",
oil: "#FF0009",
other: "#FFEFD3"
};
let color;
switch (regime) {
case "Hydraulique":
color = ENERGY_COLORS.hydroelectric;
break;
case "Thermique":
color = ENERGY_COLORS.nuclear;
break;
default:
color = ENERGY_COLORS.other;
break;
}
color = chroma(color)
.alpha(0.667)
.rgba();
color[3] *= 255;
return color;
}
function _onClick(info) {
if (info.object) {
// eslint-disable-next-line
alert(
`${info.object.properties.NOM} (${info.object.properties.PROVINCE}) - ${
info.object.properties.PUISSANCE
}MW`
);
}
}
function Map({ mainProjects }) {
const { viewport, setViewport, onLoad } = useContext(MapContext);
const [airports, setAireports] = useState();
const [electricalEnergy, setElectricalEnergy] = useState();
const [hospitals, setHospitals] = useState();
const [roads, setRoads] = useState();
useEffect(() => {
if (mainProjects.length) {
setHospitals(mainProjects[0].hospitals);
setAireports(mainProjects[1].aeroports);
setElectricalEnergy(mainProjects[2].electricite);
setRoads(mainProjects[2].routes);
}
}, [airports, electricalEnergy, hospitals, roads]);
const layers = [
//ENERGIE ELECTRIQUE
new GeoJsonLayer({
id: "energy",
data: electricalEnergy,
// Styles
filled: true,
pointRadiusMinPixels: 20,
opacity: 1,
pointRadiusScale: 2000,
getRadius: energyItem => energyItem.properties.puissance * 3.14,
getFillColor: energyItem =>
getEnergyFillColor(energyItem.properties.regime),
// Interactive props
pickable: true,
autoHighlight: true,
onClick: _onClick
}),
//AEROPORTS
new GeoJsonLayer({
id: "airports",
data: airports,
// Styles
filled: true,
pointRadiusMinPixels: 20,
opacity: 1,
pointRadiusScale: 2000,
getRadius: energyItem => energyItem.properties.PUISSANCE * 3.14,
getFillColor: energyItem =>
getEnergyFillColor(energyItem.properties.REGIME),
// Interactive props
pickable: true,
autoHighlight: true,
onClick: _onClick
}),
//HOSPITALS
new GeoJsonLayer({
id: "hospitals",
data: hospitals,
// Styles
filled: true,
pointRadiusMinPixels: 20,
opacity: 1,
pointRadiusScale: 2000,
getRadius: energyItem => energyItem.properties.PUISSANCE * 3.14,
getFillColor: energyItem =>
getEnergyFillColor(energyItem.properties.REGIME),
// Interactive props
pickable: true,
autoHighlight: true,
onClick: _onClick
}),
//ROUTES
new GeoJsonLayer({
id: "roads",
data: roads,
pickable: true,
stroked: false,
filled: true,
extruded: true,
lineWidthScale: 20,
lineWidthMinPixels: 2,
getFillColor: [160, 160, 180, 200],
getLineColor: d => [255, 160, 20, 200],
// getLineColor: d => colorToRGBArray(d.properties.color),
getRadius: 100,
getLineWidth: 1,
getElevation: 30,
onHover: ({ object, x, y }) => {
// const tooltip = object.properties.name || object.properties.station;
/* Update tooltip
http://deck.gl/#/documentation/developer-guide/adding-interactivity?section=example-display-a-tooltip-for-hovered-object
*/
},
onClick: _onClick
})
];
return (
<>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.css"
rel="stylesheet"
/>
<DeckGL
initialViewState={viewport}
viewState={viewport}
controller={true}
layers={layers}
onLoad={onLoad}
onViewportChange={nextViewport => setViewport(nextViewport)}
>
<StaticMap mapboxApiAccessToken={MAPBOX_TOKEN} mapStyle={mapStyle} />
</DeckGL>
</>
);
}
const mapStateToProps = ({
selectedLinks: { sectorSelected, provinceSelected, subSectorSelected },
mainProjects
}) => {
if (sectorSelected || provinceSelected || subSectorSelected) {
return {
mainProjects
};
} else {
return {
mainProjects: []
};
}
};
export default connect(mapStateToProps)(Map);
In the above code,i try to update my local state values by is setters,but useEffect doesn't seem to work.And it looks like it's only called once,at when the component renders for the first time.How can i solve this problem?
Thank you!!
Your useEffect has a set of dependencies that donˋt match those, which are actually used.
You are setting your local state with elements of mainProjects, so useEffect will only do something when mainProjects changes.
You donˋt seem to be doing anything with your useState-Variables, so you donˋt change state, so react doesnˋt rerender.
Update: it is really important to check, that the dependency-array (2nd argument to useEffect) and the used variables inside the function (1st argument) correspond, else bad things will happen ;-)
There is an eslint-rule for that: https://www.npmjs.com/package/eslint-plugin-react-hooks

Resources