Plot graph using react-d3 - javascript

I am trying to make a histogram of some data that I have using react-d3-components . The code that I have is like this
import React, { Component } from 'react';
import * as d3 from 'd3';
import * as ReactD3 from 'react-d3-components';
import propTypes from 'prop-types';
var axios=require('axios');
var BarChart=ReactD3.BarChart;
class App extends Component {
constructor(props){
super(props);
this.state={
data:[],
label:'',
values:[],
x:'',
y:''
}
}
componentDidMount(){
this.loadData();
}
loadData(){
var me=this;
axios({
method:'GET',
url:'https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=SPY&apikey=2QEL3WC4KITIBISR',
}).then(function(response){
var values=[];
var data=[];
Object.keys(response.data['Monthly Time Series']).forEach(function(k){
var y=response.data['Monthly Time Series'][k]["1. open"];
me.setState({
label:k,
x:k,
y:y
})
})
values.push({"x":me.state.x,"y":me.state.y});
data.push({"label":me.state.x,"values":values});
me.setState({
data:data
})
console.log(me.state.data);
}).catch(function(error){
console.log(error);
})
}
render() {
return (
<div>
<BarChart data={this.state.data} width={100} height={100}/>
</div>
)
}
}
App.propTypes={
values:React.PropTypes.array
}
export default App;
With the above code, I get this error
The documentation that I am referring to is this
The value of data in console.log(me.state.data) is this [{…}]
0
:
label
:
"2000-02-29"
values
:
Array(1)
0
:
{x: "2000-02-29", y: "139.8000"}
length
:
1
__proto__
:
Array(0)
__proto__
:
Object
length
:
1
__proto__
:
Array(0)

It seems problem in manipulating data format as per graph needs. I haven't tested it yet, but this should work.
class App extends Component {
state = {
label: 'Monthly Dates',
values: []
};
componentDidMount() {
this.loadData();
}
loadData() {
axios({
method: 'GET',
url: 'https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=SPY&apikey=2QEL3WC4KITIBISR'
})
.then(function(response) {
const values = [];
if (response && response.data && response.data['Monthly Time Series']) {
Object.keys(response.data['Monthly Time Series']).forEach((keys) => {
if (keys) {
const pointValue = {
x: String(keys),
y: Number(response.data['Monthly Time Series'][keys]['1. open'])
}
values.push(pointValue);
}
});
this.setState({values});
}
})
.catch(function(error) {
console.log(error);
});
}
getGraphData() {
const { label, values } = this.state;
return [{ label, values }]
}
renderGraph() {
if (this.state.values && this.state.values.length) {
return (
<BarChart
data={this.getGraphData()}
width={1000}
height={400}
margin={{top: 10, bottom: 50, left: 50, right: 10}}
/>
);
}
return '';
}
render() {
return (
<div>
{this.renderGraph()}
</div>
)
}
}
UPDATE: you should not render your BarChart until data is present. As BarChart is expecting a valid data format as below.
[{
label: 'somethingA',
values: [{x: 'SomethingA', y: 10}, {x: 'SomethingB', y: 4}, {x: 'SomethingC', y: 3}]
}];

Related

ReactJS populating NVD3Chart with data from an API call

I cannot get my data to populate my line chart.
The data from the API call comes back fine but when I try to pass that data into the chart it is of length 0. So I assume there is some issue with the response got being called at the right time? But I have tried so many times to sort this and nothing ever works?
import React from 'react';
import NVD3Chart from 'react-nvd3';
import SamplePage from './SamplePage';
function getData(){
const alpha = require('account')({ key: 'xxxxxxxxxxxx' });
var array = [];
alpha.data.intraday(`msft`).then((data) => {
const polished = alpha.util.polish(data);
{Object.keys(polished.data).map((key) => (
array.push(polished.data[key].open)
))}
});
return array;
}
function getDatum() {
let dataArray = getData();
let newArray = [];
for (let index = 0; index < dataArray.length; index++) {
const element = dataArray[index];
newArray.push({
'x': index,
'y': parseFloat(element)
})
}
return [
{
data: newArray,
key: 'OpenPrice',
color: '#A389D4'
}
];
}
class LineChart extends React.Component {
constructor(props) {
super(props);
this.state = {
DataisLoaded: false,
data:[]
};
}
componentDidMount() {
this.state.data = getDatum();
this.setState(
{
DataisLoaded: true,
data: this.state.data
}
)
}
render() {
const { DataisLoaded } = this.state;
if (!DataisLoaded) return <div>
<h1> Please wait.... </h1> </div>;
return (
<div>
{
React.createElement(NVD3Chart, {
xAxis: {
tickFormat: function(d){ return d; },
axisLabel: 'Time (ms)'
},
yAxis: {
axisLabel: 'Voltage (v)',
tickFormat: function(d) {return parseFloat(d).toFixed(2); }
},
type:'lineChart',
datum: this.state.data,
x: 'x',
y: 'y',
height: 300,
renderEnd: function(){
console.log('renderEnd');
}
})
}
</div>
)
}
}
export default LineChart;

Rendering the content asynchronously in reactJS

Basically I have a set of dynamic tables that are being displayed based on the values passed. If there is an empty array passed, it should show No data found. In my case when I send data to the table, all the tables will show "No data found" first then followed by the actual table content. I am not sure what is causing this.
The data is loaded asynchronously , it shows no data found and then the actual content. I have added setInterval to show this asynchronous nature
Sandbox:https://codesandbox.io/s/react-table-row-table-ryiny?file=/src/index.js:0-1322
Can someone help me?
Parent
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
const d1 = [{ name: "test", age: "20" }, { name: "test1", age: "15" }];
const d2 = [{ area: "area", pin: "123" }, { area: "area1", pin: "1245" }];
const c1 = [
{ Header: "Name", accessor: "name" },
{ Header: "Age", accessor: "age" }
];
const c2 = [
{ Header: "Area", accessor: "area" },
{ Header: "Pin", accessor: "pin" }
];
const d3 = [];
const c3 = [];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data1: [],
column1: [],
data2: [],
column2: [],
data3: [],
column3: []
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
data1: d1,
column1: c1
});
}, 2000);
setTimeout(() => {
this.setState({
data2: d2,
column2: c2
});
}, 2500);
this.setState({
data3: d3,
column3: c3
});
}
render() {
return (
<>
<DataGrid data={this.state.data1} columns={this.state.column1} />
<DataGrid data={this.state.data2} columns={this.state.column2} />
<DataGrid data={this.state.data3} columns={this.state.column3} />
</>
);
}
}
Child
import * as React from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
export default class DataGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
showMore: false
};
}
toggleState = () => {
this.setState(prevState => ({
showMore: !prevState.showMore
}));
};
formatData = () => {
let arr = [];
if (this.props.data && this.props.data.length > 0)
arr = this.state.showMore ? this.props.data : this.props.data.slice(0, 2);
return arr;
};
render() {
const { showMore } = this.state;
const { data, columns } = this.props;
const showLink = data.length > 2;
const subset = this.formatData();
return (
<>
{showLink && (
<button onClick={this.toggleState}>
Show {showMore ? "Less" : "More"}
</button>
)}
{data && data.length > 0 ? (
<ReactTable
showPagination={false}
data={subset}
columns={columns}
minRows={0}
NoDataComponent={() => null}
loading={false}
/>
) : (
"No data found"
)}
</>
);
}
}
Adding few points to the above answer.
The reason it was behaving in that way is not because of the asynchronous behavior but the life-cycle nature of the React component.which in this case takes place as:
The DataGrid is rendered with initial state of data i.e empty[] array.
No data is shown because empty[] array is passed in this cycle.
Then you are setting the state in componentDidMount.
To show the effect Datagrid is again re rendered with actual data.
Initialize the App state's data with null instead of an empty array (sandbox):
this.state = {
data1: null,
column1: [],
data2: null,
column2: [],
data3: null,
column3: []
};
In the DataGrid method check if the value is falsy (null counts, but empty array is truthy), and return null (nothing to render) if it is:
render() {
const { data, columns } = this.props;
if (!data) return null;

How to save the previous state and current state for a barchart in reactjs

I built a webapp where the user enters some text and we get some scores and create a barchart based on those scores. However I want to create a grouped barchart now for comparison. Suppose the user change something in the text and we get new score so i want to show this new scores in the bar chart with the previous score. It should compare only the current value and the previous value and not go farther than that. I tried to code this but got error like
codesandbox
ReferenceError: Cannot access 'current' before initialization
Editor.js I'm just pasting important parts of the code since it's a big file
import React, { Component, useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Button, Tabs, Tab, Modal } from "react-bootstrap";
import BarChart from "../barCharts";
const style2 = {
flex: 1,
textAlign: "center"
};
class MainText extends Component {
state = {
analysis: {
tonal: [],
},
showButton: false
};
Analysis = async () => {
const { enteredText } = this.state;
const body = { snippetdesc: enteredText };
const stringifiedBody = JSON.stringify(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: stringifiedBody
};
const url = `http://localhost:5000/api/apicall`;
try {
const response = await fetch(url, options);
const result = await response.json();
const {
...tonalAnalysis
} = result.scores;
const tonalArray = Object.entries(tonalAnalysis).reduce(
(carry, [tone, value]) => [
...carry,
{ tone: tone.toLowerCase(), value: parseInt(value) }
],
[]
);
this.setState({
analysis: { ...this.state.analysis, tonal: tonalArray },
showButton: true
});
} catch (error) {
console.error("error when performing sentiment analysis");
}
};
render() {
const series = this.state.analysis.tonal.map((tonal) => tonal.value);
const prev=current;
const current = series; //error here
return (
<div>
//<textarea>
//analysis button
<Button
className={this.state.showButton ? "showButton" : "hideButton"} //.showButton{visibility: visible}; .hideButton{visibility: hidden}
onClick={this.showPieModal}
>
Show BarChart Modal
</Button>
{this.state.showPieModal && (
<Modal show={this.state.showPieModal} onHide={this.handleClosePie}>
<Modal.Header closeButton>
<Modal.Title>Analysis</Modal.Title>
</Modal.Header>
<Modal.Body className="pieModal">
<BarChart data={current} prev={prev}/>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.handleClosePie}>
Close
</Button>
</Modal.Footer>
</Modal>
)}
</div>
);
}
}
export default MainText;
BarChart.js
import React from "react";
import { Bar } from "react-chartjs-2";
import "./App.css";
class BarChart extends React.Component {
constructor(props) {
super(props);
console.log("props" + JSON.stringify(this.props));
this.state = {
data: {
labels: [
"January",
"February",
"March",
],
datasets: [
{
label: "My First dataset",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
//stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: this.props.data
},
{
label: "My second dataset",
backgroundColor: "rgba(155,231,91,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
//stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: this.props.prev
}
]
}
};
}
render() {
const options = {
responsive: true,
legend: {
display: false
},
type: "bar",
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
};
return (
<Bar
data={this.state.data}
options={options}
width={null}
height={null}
options={options}
/>
);
}
}
export default BarChart;
Error explains it, you are trying to access a constant before you initialize it. const current = series; is after const prev = current;.
But the problem is you are trying to render the previous values without storing them in state. What if your component re-renders? The previous data will be lost.
You want to keep the previous values in state:
state = {
analysis: {
tonal: [],
},
prevAnalysis: {
tonal: [],
},
showButton: false,
};
Update when you receive new data:
const currAnalysis = // clone this.state.analysis
this.setState({
prevAnalysis: currAnalysis,
analysis: { ...this.state.analysis, tonal: tonalArray },
showButton: true
});
Then use the prevAnalysis in your component as prev. You don't need this
const prev = current;
const current = series;
Generally, everything that is not saved in state will be used for the current render, whenever you want to keep previous values etc. in memory, you store them in state.
You have error in your code here:
render() {
const series = this.state.analysis.tonal.map((tonal) => tonal.value);
const prev=current;
const current = series; //error here
Your code trying to access value current, and after that you are declaring it using const.

map function value not displaying

In console.log the api fetched data are displaying but in browser itis
showing only white screen. In map function have to update the state function
import React, { Component } from 'react';;
import * as algoliasearch from "algoliasearch";
class App extends React.Component {
constructor() {
super();
this.state = {
data: { hits: [] }
}
// set data to string instead of an array
}
componentDidMount() {
this.getData();
}
getData() {
var client = algoliasearch('api-id', 'apikey');
var index = client.initIndex('');
//index.search({ query:""}, function(data){ console.log(data) })
//index.search({ query:""}, function(data){ console.log("DataRecib=ved. First check this") })
index.search({
query: "",
attributesToRetrieve: ['ItemRate', 'Color'],
hitsPerPage: 50,
},
function searchDone(error, data) {
console.log(data.hits)
});
}
render() {
return (
<div id="root">
{
this.state.data.hits.map(function (data, index) {
return
<h1>{this.setState.data.ItemRate}<br />{data.Color}</h1> >
})}
</div>
);
}
}
//render(<App />, document.getElementById('app'));
export default App;
Couple of mistakes -:
You just need to use this.state.data.ItemRate instead of this.setState.data.ItemRate.
You can get state inside .map using arrow functions ( . )=> { . }
Visit https://www.sitepoint.com/es6-arrow-functions-new-fat-concise-syntax-javascript/
render() {
return (
<div id="root" >
{
this.state.data.hits.map((data,index) => {
return<h1>{this.state.data.ItemRate}<br />{data.Color}</h1>
}
Every this.setState triggers a render() call. If you setState inside render method, you go into an infinity loop.
You want to update this.state.data.hits inside getData() function, then you can display the data like so:
this.state.data.hits.map(data =>
<h1>{data.Color}</h1>
)
For example, if console.log(data.hits) logs out the correct data, then you can:
this.setState({
data: {
hits: data.hits
}
})
EDIT:
Using the code you provided, it should be like this:'
getData = () => {
var client = algoliasearch('A5WV4Z1P6I', '9bc843cb2d00100efcf398f4890e1905');
var index = client.initIndex('dev_twinning');
//index.search({ query:""}, function(data){ console.log(data) })
// index.search({ query:""}, function(data){ console.log("Data Recib=ved. First check this") })
index.search({
query: "",
attributesToRetrieve: ['ItemRate', 'Color'],
hitsPerPage: 50,
}, searchDone = (error, data) => {
this.setState({
data: {
hits: data.hits
}
})
console.log(data.hits)
})
}

React update state array object single property

I have the following method:
addWidget = (index) => {
var currentState = this.state;
if(currentState.availableWidgets[index].pos === 'start'){
// add it at the start
for(var i = 0; i < currentState.widgets.length; i++){
this.setState({
widgets: [
...currentState.widgets,
currentState.widgets.x = 5
]
})
}
}
else {
var endX = currentState.widgets.reduce((endX, w) => endX + w.w, 0)
if (endX === 12) endX = 0
this.setState({
widgets: currentState.widgets.concat({
...currentState.availableWidgets[index],
i: uuid(),
x: endX,
y: Infinity,
})
})
}
console.log(currentState.widgets);
}
and the state is:
class DashboardContainer extends React.Component {
state = {
widgets: [],
availableWidgets: [
{
type: 'compliance-stats',
config: {
},
w: 1,
h: 1,
pos: 'start',
},
{
type: 'compliance-stats',
config: {
},
w: 3,
h: 2,
}
]
}
...
I am trying to update the "x" property of each object inside "widgets" by doing so:
for(var i = 0; i < currentState.widgets.length; i++){
this.setState({
widgets: [
...currentState.widgets,
currentState.widgets.x = 5
]
})
}
I am aware of setting the state inside a loop is not good at all. However I am currently getting an error.
I am importing widgets in:
const Dashboard = ({ widgets, onLayoutChange, renderWidget }) => {
const layouts = {
lg: widgets,
}
return (
<div>
<ResponsiveReactGridLayout
layouts={layouts}
onLayoutChange={onLayoutChange}
cols={{ lg: 12 }}
breakpoints={{lg: 1200}}>
{
widgets.map(
(widget) =>
<div key={widget.i}>
{renderWidget(widget)}
</div>
)
}
</ResponsiveReactGridLayout>
</div>
)
}
Probably better to change the widgets and then setState only once:
const changedWidgets = currentState.widgets.map(w => ({ ...w, x: 5 }));
this.setState({ widgets: changedWidgets });
The spread operator inside an array will concatenate a new value onto the array, so by seting widgets state to [ ...currentState.widgets, currentState.widgets.x = 5 ] what you're actually trying to do is currentState.widgets.concate(currentState.widgets.x = 5) which is throwing your error.
If you would like to modify the value inside an array you should map out the array and then modify the objects inside the array like so.
const widgets = currentState.widgets.map(widget => { ...widget, x: 5})
this.setState({ widgets })

Categories