ReactJS populating NVD3Chart with data from an API call - javascript

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;

Related

Why this.state is not working in render()?

componentDidMount() {
this.fetchApi();
}
fetchApi() {
const pointerToThis = this;
const API_KEY = "";
let API_CALL = `https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=IBM&interval=5min&apikey=${API_KEY}`;
let stockChartXValuesFunction = [];
let stockChartYValuesFunction = [];
fetch(API_CALL)
.then(function (response) {
return response.json();
})
.then(function (data) {
for (var key in data["Time Series(Daily"]) {
stockChartXValuesFunction.push(key);
stockChartYValuesFunction.push(
data["Time Series(Daily"][key]["1. open"]
);
}
// handleClick(e) {
// this.setState({value:e.target.value}, () => {
// console.log(this.state.value); // Updated value here
// });}
pointerToThis.setState({
stockChartXValues: stockChartXValuesFunction,
stockChartYValues: stockChartYValuesFunction,
});
});
}
render() {
return (
<div>
<Plot
data={[
{
x: this.state.stockChartXValues,
y: this.state.stockChartYValues,
type: "scatter",
mode: "lines+markers",
marker: { color: "red" },
},
]}
layout={{ width: 720, height: 440, title: "A Fancy Plot" }}
/>
</div>
);
}
}
export default Stock;
**
Why this.state is not working in render()?**
Is it because of ES6
I am new to ReactJs.I tried a small snippet with React.But this.state is not working in ES6 ReactJs.Help me what I am missing!!
The problem is that you are using function() {...} declarations rather than arrow functions () => {...}.
function declarations rebind this to the function. Where as arrow declarations keep the current this (which is the component).
To fix, just change to this:
fetch(API_CALL)
.then((response) => {
return response.json();
})
.then((data) => {
for (var key in data["Time Series(Daily"]) {
stockChartXValuesFunction.push(key);
stockChartYValuesFunction.push(
data["Time Series(Daily"][key]["1. open"]
);
}
// handleClick(e) {
// this.setState({value:e.target.value}, () => {
// console.log(this.state.value); // Updated value here
// });}
this.setState({
stockChartXValues: stockChartXValuesFunction,
stockChartYValues: stockChartYValuesFunction,
});
});
In your class constructor, you'll also need to bind the fetch method to this:
constructor(props) {
super(props);
this.state = { stockChartXValues: [], stockChartYValues: [], }
this.fetchApi = this.fetchApi.bind(this)
}

Create Dynamic Column in react.js

I'm trying to create a dynamic column in react.js any body have an idea? I've already done with the static column but I want to make them dynamic have a look on my code and please give me suggestion.
import React from 'react';
import ReactDataGrid from 'react-data-grid';
class Example extends React.Component {
constructor(props, context) {
super(props, context);
this.createRows();
this._columns = [
{ key: 'id', name: 'ID' },
{ key: 'title', name: 'Title' },
{ key: 'count', name: 'Count' } ];
this.state = null;
}
createRows = () => {
let rows = [];
for (let i = 1; i < 5; i++) {
rows.push({
id: i,
title: 'Title ' + i,
count: i * 1000
});
}
this._rows = rows;
};
rowGetter = (i) => {
return this._rows[i];
};
render() {
return (
<ReactDataGrid
columns={this._columns}
rowGetter={this.rowGetter}
rowsCount={this._rows.length}
minHeight={200} />);
}
}
export default Example;
You can try this function to create dynamic column key and name
createColumns = async (data) => {
let columns = [], keys = [];
if (data)
keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
columns.push({key: Object.keys(data)[i], name: Object.keys(data)[i]});
}
await this.setState({columns});
};

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

Plot graph using react-d3

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

setState after windows resize error with react. Trying to pass dynamic width as props

Trying to calculate width of my scatterplot on the fly so I can pass it as props to my d3 component. I'm having no problem console logging in the handleResize function that is being called by window.addEventListener('resize', this.handleResize) in my ComponentDidMount but I can't setState in the handleResize function or seem to access anything outside of it. I've also tried var self = this; to rebind it but am still getting either a this.setState is not a function or self.setState is not a function.
import React, { PropTypes, Component } from 'react';
import { getDataRange, getTeams, getFile, getAllHitsData, getPlayers} from 'api/index.js';
import {Table, Thead, Th, Tr, Td} from 'components/Reactable';
import Autosuggest from 'react-autosuggest';
import { Link } from 'react-router';
import ScatterChart from 'components/scatterchart';
import DashboardStats from 'components/dashboard-stats';
import DateRangeComponent from 'components/date-range';
import AdminSquare from 'components/admin-square';
import { connect } from 'react-redux';
import { changeStartDate, changeEndDate } from '../../redux/modules/redux-date-change';
import { sendHitData } from '../../redux/modules/send-hit.js';
import { Loading } from 'react-loading';
let allHitDatas = [];
let hitDatas = [];
let teams = [];
// let selectedTeamID = null;
// let selectedTeamName = 'all_teams';
let newFile = '';
// let teamId = '';
let players = [];
let width;
class Dashboard extends Component {
static propTypes = {
team: PropTypes.object.isRequired,
startDate: PropTypes.string.isRequired,
endDate: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired
};
static contextTypes = {
router: PropTypes.object
};
constructor(props, context) {
super(props, context);
this.state = {
showRangePicker: false,
hitDatas: [],
teams: [],
start: "",
end: "",
team: this.props.team,
selectedTeamID: null,
selectedTeamName: "",
newFileConfirmation: false,
players: [],
allHitDatas: [],
suggestions: this.getSuggestions(''),
selected: '',
showDatePickerControls: false,
// maxHic: 0
// showScatterPlot: true
};
this.onChange = this.onChange.bind(this);
this.onSuggestionsUpdateRequested = this.onSuggestionsUpdateRequested.bind(this);
}
componentWillReceiveProps() {
this.setState({
maxHic: 0,
team: this.props.team,
selectedTeamID: this.props.team.id,
startDate: this.props.startDate,
endDate: this.props.endDate
// allHitDatas: []
}, () => {
// this.getDomains();
this.dataChangeHelper();
return this.state;
});
}
componentDidMount() {
this.dataChangeHelper();
window.addEventListener('resize', this.handleResize);
getTeams().then((response) => {
teams = response.data;
this.setState({teams: teams});
});
getPlayers().then((response) => {
players = response.data;
this.setState({
players: players
}, () => {
return this.state;
});
});
}
getDomains() {
let dates = [];
let hicArray = [];
console.log(this.state.allHitDatas);
if (this.state.allHitDatas.length === 0) {
return allHitDatas.map((hitData) => {
let date = Date.parse(hitData.EventTime);
dates.push(date);
hicArray.push(hitData.Hic);
let maxDate = Math.max.apply(null, dates);
let maxHic = 0;
let minDate = Math.min.apply(null, dates);
let minHic = 0;
this.setState({
minDate: minDate,
maxDate: maxDate,
minHic: minHic,
maxHic: maxHic
}, () => {
console.log(this.state.maxHic);
return this.state;
});
});
}
return this.state.allHitDatas.map((hitData) => {
let date = Date.parse(hitData.EventTime);
dates.push(date);
hicArray.push(hitData.Hic);
let maxDate = Math.max.apply(null, dates);
let maxHic = Math.max.apply(null, hicArray);
let minDate = Math.min.apply(null, dates);
let minHic = Math.min.apply(null, hicArray);
this.setState({
minDate: minDate,
maxDate: maxDate,
minHic: minHic,
maxHic: maxHic
}, () => {
console.log(this.state.maxHic)
return this.state;
});
});
}
dataChangeHelper() {
const newConfig = {
start: this.props.startDate,
end: this.props.endDate,
team: this.props.team.id
};
getDataRange(newConfig)
.then((response) => {
hitDatas = response.data;
this.setState({
hitDatas: hitDatas
}, () => {
return this.state;
});
});
getAllHitsData(newConfig)
.then((response) => {
allHitDatas = response.data;
this.setState({
allHitDatas: allHitDatas
}, () => {
this.getDomains();
return this.state;
});
});
}
handleResize() {
// const self = this;
let elem = document.getElementById('scatter-chart');
width = elem.offsetWidth * 0.9;
console.log(width);
this.setState({
scatterWidth: width
}, () => {
console.log(this.state.scatterWidth);
});
}
render () {
if (this.state.teams.length === 0 || this.state.players.length === 0) {
return (
<div className="no-data-container">
<div className="no-data-message">We don't have any data for you right now. Would you like
to add some players, teams, or devices?
</div>
<ul className="no-data-links">
<AdminSquare title="PLAYER ADMIN" icon="person" link="/player"/>
<AdminSquare title="TEAM ADMIN" icon="group" link="/team"/>
<AdminSquare title="DEVICE ADMIN" icon="sd_storage" link="/device"/>
</ul>
</div>
);
}
const { value, suggestions } = this.state;
const inputProps = {
placeholder: 'Search for a player',
value,
onChange: this.onChange
};
return (
<div>
<ScatterChart
data={this.state.allHitDatas}
domain={{x: [this.state.minDate, this.state.maxDate], y: [this.state.maxHic, 0]}}
statOneTitle="HIC"
sendHitData={(d) => this.handleImpactClick(d)}
width={ width }
/>
</div>
);
}
}
function mapStateToProps(state) {
console.log(state);
return {
startDate: state.startDate,
endDate: state.endDate
};
}
export default connect(mapStateToProps)(Dashboard);
handleResize doesn't have Dashboard object associated with 'this'. You need to bind to the event hanler
replace window.addEventListener('resize', this.handleResize); with
window.addEventListener('resize',this.handleResize.bind(this));
which will bind the this keyword..so that you can do like this.setState or access any object defined in Dashboard
If you plan on using this inside of a method and the method is not a part of react component's lifecycle, then you need to set it's context to the component instance. In your case you haven't set the correct context for the handleResize, getDomains, dataChangeHelper methods, so the constructor needs some additional bindings:
this.handleResize = this.handleResize.bind(this);
getDomains ...
dataChangeHelper ...

Categories