how to pass dynamic value in the array using donutchart in reactjs - reactjs

i am getting this response from back-end like this. how can i take this response in to state variable and how can i use the dynamic value to pass in this chart inside the data:[[ ]]array in reactjs. i am using in my code like this format. i need only dynamic value in this chart based on select the dropdown list select the one value(last day) and click on applying filter button. am sending the request and getting this (last day) response right. i need whatever value getting the response. i need to show dynamic value in this chats. based on applying the filter. how to do pass dynamic value in this data:[[]] array.
class Report extends React.Component {
constructor(props) {
super(props);
this.state = {
dashboardchart:{
series:[{
data:[]
}],
},
};
}
componentDidMount() {
UserAction._getdashboardchart();
}
_userStoreChange(type, value) {
if (type == 'DashboardChart') {
let dashboardchart = UserStore._getdashboardchart() || {};
this.setState({dashboardchart})
}
}
render() {
let chartdata = this.state.dashboardchart.series
const piechart = {
chart: {
type: 'pie',
options3d: {
enabled: true,
alpha: 45
}
},
title: {
text: 'Claim - Key Performance Indicators'
},
subtitle: {
text: ''
},
plotOptions: {
pie: {
innerSize: 100,
depth: 45
}
},
series: [{
name: 'Total consumption',
data: chartdata && chartdata.data && chartdata.data
}]
}
return (
<div className="panel-body">
<ReactHighcharts config={piechart}></ReactHighcharts>
</div>
);
}
}
export default Report;

Related

How do I get BootstrapTable to redraw with updated data?

I have a Table from react-bootstrap that displays a list of devices with several columns. I want to change this so you can reorder by the columns so I switched to BootstrapTable from react-bootstrap-table-next. But my problem is that changing the state of the Component doesn't cause the table to update - this only happens if I click on a column to reorder the table.
The code is written to create the devices_table and save it to state then call an API to get the device version and add that to the state, causing the component to redraw. But when render() is called again the additional data isn't added to the table.
I've created a working example at https://codesandbox.io/s/react-bootstrap-table-next-new-data-problem-5w0op
import React, { Component } from 'react'
import BootstrapTable from 'react-bootstrap-table-next'
class DeviceTable extends Component {
constructor(props) {
super(props)
this.state = {
devices_table: {}
}
}
componentDidUpdate(prevProps) {
if (prevProps.devices !== this.props.devices) {
let devices_table = this.props.devices.map(this.myFunction);
this.setState({
devices_table: devices_table
})
}
}
myFunction = (value, index, array) => {
let device = {};
device.device = value;
device.index = index + 1;
device.version = '';
apiGetDeviceVersion(value.identifier)
.then((res, deviceId = value.identifier) => {
let devices_table = this.state.devices_table;
let objIndex = devices_table.findIndex(d => d.device.identifier === deviceId)
devices_table[objIndex].version = res.valueReported;
this.setState({
devices_table: devices_table
})
})
.catch(e => {
console.log(e)
})
return device;
}
render() {
const devices = this.state.devices_table;
if (isEmpty(devices)) {
return (<div></div>)
}
let columns = [
{
text: "#",
dataField: "index",
sort: true
},
{
text: "ID",
dataField: "device.identifier",
sort: true
},
{
text: "Name",
dataField: "device.name",
sort: true
},
{
text: "Status",
dataField: "device.status",
sort: true
},
{
text: "Version",
dataField: "version",
sort: true
}
];
return (
<div>
<BootstrapTable keyField={"device.identifier"} data={devices} columns={columns}></BootstrapTable>
</div>
)
}
}
export default DeviceTable
componentDidUpdate(prevProps) {
if (prevProps.devices !== this.props.devices) {
*let devices_table* = this.props.devices.map(this.myFunction);
this.setState({
devices_table: devices_table
})
}
}
It may happen due to response is working as asynchronous function(because of API). Thus, you have to wait for the response to make it available, otherwise "devices_table" remains empty or undefined and leads to no data visualization.

React ChartJS prevent new data from being added into state after it's redrawn?

I'm trying to update my chart using react-chartjs-2. I'm using date picker to filter different data and re-render the chart accordingly such as display data today, yesterday, last 7 days etc.. The data is being fetched from my database
However, when the chart gets redrawn, and re-rendered, it gets added onto the state which I don't want. I simply want to re-render the new data that was requested not re-render and add onto the old data that was in the chart.
I actually fixed this issue before with vanilla JavaScript because I wasn't using react I used the destroy() method that the chart documentation says to use but i'm not sure how to use it with react?
So upon further inspection, it seems my chart is re-rendering fine. However, when it gets re-rendered, additional data is being added onto my chartData state which I don't want, I just want the new data that got requested to show on the chart. I'm still trying to figure that portion out.
Here is my code I have a lot of it so I'll only show the relevant parts:
import React from "react";
import reportsService from "../../services/reportsService";
import update from "react-addons-update";
import moment from "moment";
import { Bar } from "react-chartjs-2";
import "chartjs-plugin-labels";
import "chartjs-plugin-datalabels";
class Reportspage extends React.Component {
constructor(props) {
super(props);
this.state = {
chartData: {
labels: [],
datasets: [
{
//label: "Quotes",
data: [],
backgroundColor: []
}
]
}
};
}
chartColors() {
let colors = [];
for (let i = 0; i < 100; i++) {
let r = Math.floor(Math.random() * 200);
let g = Math.floor(Math.random() * 200);
let b = Math.floor(Math.random() * 200);
let c = "rgb(" + r + ", " + g + ", " + b + ")";
colors.push(c);
}
// Update deep nested array of objects in state
this.setState({
chartData: update(this.state.chartData, {
datasets: { 0: { backgroundColor: { $set: colors } } }
})
});
}
datePicker() {
let _this = this;
let start = moment().subtract(29, "days");
let end = moment();
let showReports;
let data;
let reloNames = [];
let reloCount = [];
function focusDate(start, end) {
$("#daterangePicker span").html(
start.format("MMMM D, YYYY") + " - " + end.format("MMMM D, YYYY")
);
}
$("#daterangePicker").daterangepicker(
{
startDate: start,
endDate: end,
ranges: {
Today: [moment(), moment()],
Yesterday: [
moment().subtract(1, "days"),
moment().subtract(1, "days")
],
"Last 7 Days": [moment().subtract(6, "days"), moment()],
"Last 30 Days": [moment().subtract(29, "days"), moment()],
"This Month": [moment().startOf("month"), moment().endOf("month")],
"Last Month": [
moment()
.subtract(1, "month")
.startOf("month"),
moment()
.subtract(1, "month")
.endOf("month")
]
}
},
focusDate
);
focusDate(start, end);
$("#daterangePicker").on("apply.daterangepicker", async function(
event,
picker
) {
switch (picker.chosenLabel) {
case "Today":
showReports = await reportsService.reloQuotes({
reportStatus: "Today"
});
data = showReports.recordsets[0];
data.forEach((element, index, array) => {
reloNames.push(element.reloNames);
reloCount.push(element.NoofOrders);
});
_this.setState({
chartData: update(_this.state.chartData, {
labels: { $set: reloNames },
datasets: { 0: { data: { $set: reloCount } } }
})
});
console.log(_this.state);
break;
case "Yesterday":
showReports = await reportsService.reloQuotes({
reportStatus: "Yesterday"
});
data = showReports.recordsets[0];
data.forEach((element, index, array) => {
reloNames.push(element.reloNames);
reloCount.push(element.NoofOrders);
});
_this.setState({
chartData: update(_this.state.chartData, {
labels: { $set: reloNames },
datasets: { 0: { data: { $set: reloCount } } }
})
});
console.log(_this.state);
break;
case "Last 7 Days":
showReports = await reportsService.reloQuotes({
reportStatus: "Last 7 Days"
});
data = showReports.recordsets[0];
data.forEach((element, index, array) => {
reloNames.push(element.reloNames);
reloCount.push(element.NoofOrders);
});
_this.setState({
chartData: update(_this.state.chartData, {
labels: { $set: reloNames },
datasets: { 0: { data: { $set: reloCount } } }
})
});
console.log(_this.state);
break;
}
});
//console.log(this.state);
}
async reloQuotes() {
const showreloQuotes = await reportsService.reloQuotes();
let data = showreloQuotes.recordsets[0];
let reloNames = [];
let reloCount = [];
data.forEach((element, index, array) => {
reloNames.push(element.reloNames);
reloCount.push(element.NoofOrders);
});
this.setState({
chartData: update(this.state.chartData, {
labels: { $set: reloNames },
datasets: { 0: { data: { $set: reloCount } } }
})
});
}
async componentDidMount() {
await this.chartColors();
await this.datePicker();
// Execute models real time thus re-rendering live data on the chart real time
await this.reloQuotes();
}
render() {
return (
<div className="fluid-container">
<div className="container">
<h1>Reports</h1>
<div className="row">
<div className="daterangeContainer">
<div
id="daterangePicker"
style={{
background: "#fff",
cursor: "pointer",
padding: "5px 10px",
border: "1px solid #ccc",
width: "100%"
}}
>
<i className="fa fa-calendar" />
<span /> <i className="fa fa-caret-down" />
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<Bar
data={this.state.chartData}
height={800}
options={{
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
xAxes: [
{
ticks: {
beginAtZero: true,
autoSkip: false
},
scaleLabel: {
display: true
}
}
]
},
title: {
display: true,
text: "Quotes",
fontSize: 16
},
plugins: {
datalabels: {
display: true,
color: "white"
}
}
}}
redraw
/>
</div>
</div>
</div>
</div>
);
}
}
export default Reportspage;
So the problem that you describe is that data is added to your component state when the component is re-rendered. In the code snipped you have supplied, you dont use any of the life cycle methods in React that can be triggered on re-renders. And I cannot see any other hooks that should trigger on a re-render. Therefore I cant find any source to your problem.
However, I can see other issues that might make debugging harder for you. Solving these might help you in nailing down the actual problem. In the componentDidMount method you call functions whose only purpose is to update the state. This is not good design since it will force the component to immediately re-render several times whenever it is mounted.
A better design is to fully prepare the chartData-objekt in the constructor. For example, you could change your chartColors function to take a chartData object as a parameter, and return the new object with colors added. Then make your constructor look something like this:
constructor(props) {
super(props);
const chartDataWithoutColors = {
labels: [],
datasets: [
{
//label: "Quotes",
data: [],
backgroundColor: []
}
]
}
const chartDataWithColor = this.chartColors(chartDataWithoutColors);
this.state = {
chartData: chartDataWithColor
};
}
By removing unnecessary calls to setState you will make your components life both simpler and more performant. When you have simplified your component, start debugging by removing non-critical parts one at the time and try to nail down when your problem disappears. This should be sufficient to find the bug.
Good luck!

Variable access in ReactJS

why i cant access to variable "rows" in method parse?
If i put this.setState({rows: [3,2]}); outside of "parseString it works, but what to do to make it work inside this method?
class App extends React.Component {
constructor(props) {
super(props);
this.state = this.getCleanState();
this.parse = this.parse.bind(this);
}
getCleanState() {
return {
isLoading: true,
columns:[
{ name: 'a:ClientID', title: 'ID kombinace'},
{ name: 'a:Name', title: 'Název kombinace'},
],
defaultColumnWidths: [
{ columnName: 'a:ClientID', width: 160 },
{ columnName: 'a:Name', width: 200 },
],
rows:[],
selection: [],
};
}
parse(XML){
var parseString = require('react-native-xml2js').parseString;
var xml = XML;
parseString(xml, function (err, result) {
console.dir(result);
this.setState({rows: [3,2]});
});
}
because you use an anonymous function that is not binded.
Use arrow function for instance (or explicitly bind)
parse(XML){
var parseString = require('react-native-xml2js').parseString;
var xml = XML;
parseString(xml, (err, result) => {
console.dir(result);
this.setState({rows: [3,2]});
});
}

react.js rendering image tag from a function

I have a bar chart created with d3.js and am trying to display an image tag for each tick in x axis. For example,
class Test extends Component {
constructor(props) {
super(props);
this.state = {
data: null
}
this.renderAxis = this.renderAxis.bind(this);
}
componentDidMount() {
this.setState({
data: [
{
name: 'hello',
imgUrl: 'https://img.com/someImageUrl',
value: 100
},
{
name: 'ola',
imgUrl: 'https://img.com/someImageUrl2,
value: 50
}
]
})
}
componentDidUpdate(){
this.renderAxis();
}
renderAxis() {
this.x.domain(this.state.dummyData.map( d => d.name )) // <-- this one works
this.x.domain(this.state.dummyData.map( d => {
return (
<img src={d.imgUrl} style={{'width': '30px'}}/>
)
})); // <-- this one does not work
this.y.domain([0, 100]);
...
So above is my code and I believe you already know what I am trying to do.
I thought the way I did would render the image instead of plain text as tick labels, but..
How should I render that image tag in this case ..?

React Component, Highstock: Synchronize multiple charts?

I am working with React and HighCharts. I am relatively new to both these technologies. I need to generate two synchronized HighStock charts. I was able to display the charts with the below layout.
<div class=container>
<div class=chart1>new highcharts.StockChart(newChartOptions) </div>
<div class=chart2>new highcharts.StockChart(newChartOptions)</div>
</div>
The Charts are displayed. I want to synchronize the charts to have a common tool tip, I see the http://www.highcharts.com/demo/synchronized-charts , not sure how to implement with React. I have tried to assign a function(handleEvent(e)) to plotOptions:{line:{ point:{ event:{click: and MouseOver}}}} but it did not help. Not sure how to invoke the handleEvent(e) method. I am not sure how/when to invoke the handleEvent(e). Any help is greatly is appreciated.
Below is the Component code:
import $ from 'jQuery';
import React from 'react';
import highcharts from 'highcharts-release/highstock';
export default class SynchronizedStatusChart extends React.Component {
constructor (props) {
super(props);
this.state = {
chartName: `chart${this.props.chartNum}`,
};
}
handleEvent(e){
let allCharts = highcharts.charts;
console("SynchronizedStatusChart:handleEvent:ChartsLength = " + allCharts.length);
var chart, point, i, event;
for (i = 0; i < allCharts.length; i = i + 1)
{
chart = highcharts.charts[i];
event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this);
}
}
}
componentDidMount () {
}
componentWillUpdate (nextProps) {
for(let i=0; i<nextProps.data.length; i++){
this.generateChart(nextProps.data[i],i+1,nextProps.titles[i]);
}
}
generateChart(data, i, title) {
if(data == null)
{
data = [];
}
let ticksData = [0,1];
let newChartOptions =
{
chart: {
//renderTo: document.getElementById(this.state.chartName),
renderTo: document.getElementById(`SyncChart${i}`),
height:'125'
},
rangeSelector: {
enabled: false
},
credits: {
enabled: false
},
navigator: {
enabled: false
},
scrollbar: {
enabled: false
},
tooltip: {
shared: true,
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y:.2f}</b> <br/>'
},
xAxis:{
},
yAxis: {
offset: 15,
labels: {
align: 'center',
x: -3,
y: 6
},
tickPositioner: function () {
var positions = ticksData;
return positions;
},
opposite:false,
showLastLabel: true,
title:{
text:title
}
},
series: [{
name: title,
type: this.props.status ? 'area' : 'line',
data: data,
showInNavigator: false
}],
};
new highcharts.StockChart(newChartOptions);
}
render () {
return (
<div className="med-chart col-md-9" id={this.state.chartName} style={this.props.chartStyle}>
<div id='SyncChart1'></div>
<div id='SyncChart2'></div>
</div>
);
}
}
I had the same problem recently. Here's what has worked for me.
I am using common parent component to add pure javascript event listeners for 'mousemove' and 'mouseleave' to each chart DOM element.
class ParentComponent extends Component {
...
componentDidMount() {
this.charts = document.querySelectorAll('.chart-container');
[].forEach.call(this.charts, (chart) => {
chart.addEventListener('mousemove', this.handleChartMouseMove);
chart.addEventListener('mouseleave', this.handleChartMouseLeave);
});
Highcharts.Pointer.prototype.reset = () => undefined;
}
componentWillUnmount() {
[].forEach.call(this.charts, (chart) => {
chart.removeEventListener('mousemove', this.handleChartMouseMove);
chart.removeEventListener('mousemove', this.handleChartMouseLeave);
});
}
handleChartMouseMove = (e) => {
const chartIndex = e.currentTarget.dataset.highchartsChart;
const chart = Highcharts.charts[chartIndex];
const event = chart.pointer.normalize(e);
const pointIndex = chart.series[0].searchPoint(event, true).index;
Highcharts.charts.forEach((chart) => {
const xAxis = chart.xAxis[0];
const point = chart.series[0].points[pointIndex];
const points = chart.series.map(s => s.points[pointIndex]); // if more than one series
point.onMouseOver();
xAxis.drawCrosshair(event, point);
// if more than one series, pass an array of points, took a me a long time to figure it out
chart.tooltip.refresh(points, event);
});
};
handleChartMouseLeave = () => {
Highcharts.charts.forEach((chart) => {
chart.tooltip.hide();
chart.xAxis[0].hideCrosshair();
});
};
...
}

Resources