I am using react js. I want to make a chart using chartjs from the data I fetched from the api. Here, I just want to plot the chart between the below two arrays.
symbolsdateforchart: []
closingdateforchart: []
But, I am not sure how to use the value of these (please have a look at my code) since it has not been assigned yet, and the value get assigned in componentDidMount() and I making my chart inside this.state. Is there a better way to make the chart using chartjs between the above two array I mentioned. Or can I make this work somehow? Any help is appreciated.
My code:
import React from "react";
import { Line } from "react-chartjs-2";
import Chart from "./Chart";
export default class Symbols extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
symbolsdateforchart: [],
closingdateforchart: [],
search: "",
symbol: this.props.location.symbol,
//here I am making my chart
chartData: {
labels: ["how to add the data of symbolsdateforchart here"], //and here I know the data is not yet assigned to symbolsdateforchart: [], so not sure how to make it work
datasets: [
{
label: "Closing price",
data: ["how to add the data of closingdateforchart here"], //and here I know the data is not yet assigned to closingdateforchart: [], so not sure how to make it work
fill: false,
backgroundColor: [
"rgba(255,99,132,0.6)",
"rgba(54,162,235,0.6)",
"rgba(255,206,86,0.6)",
"rgba(75,192,192,0.6)",
"rgba(153,102,255,0.6)",
"rgba(255,159,64,0.6)",
"rgba(255,99,132,0.6)",
],
},
],
},
};
}
componentDidMount() {
let symbolsdate = [];
let closingprice = [];
fetch(`http://131.181.190.87:3001/history?symbol=${this.state.symbol}`)
.then((res) => res.json())
.then((json) => {
console.log(json);
for (const dataobj of json) {
let tempsymbolsDate = dataobj.timestamp.split("T")[0];
let tempclosingPrice = dataobj.close;
let tempArray = tempsymbolsDate.split("-");
tempsymbolsDate =
tempArray[2] + "/" + tempArray[1] + "/" + tempArray[0];
symbolsdate.push(tempsymbolsDate);
closingprice.push(tempclosingPrice);
}
this.setState({
isLoaded: true,
items: json,
//here the value get assigned for them
symbolsdateforchart: symbolsdate,
closingdateforchart: closingprice,
});
});
}
render() {
return (
//here I just print the table using the api I fetched in omponentDidMount()
);
}
}
In componentDidMount() you can update state variable chartData as
this.setState({
isLoaded: true,
items: json,
chartData: {
labels: symbolsdate,
datasets: [{...this.state.chartData.datasets, data: closingprice}]
}
});
Better is to separate dynamic part from static. Store static data like backgroundColor outside of state & provide statically in HTML of render() while, use state variables for dynamic data.
Related
Unable to get data when using array index with contentful cms
constructor() {
super();
this.state = {
partners: [],
}
}
client = contentful.createClient({
space: process.env.REACT_APP_CONTENTFUL_SPACE_ID,
accessToken: process.env.REACT_APP_CONTENTFUL_ACCESS_TOKEN
})
componentDidMount() {
this.client.getEntries({
'content_type': 'partnersCollection',
'order': 'sys.createdAt',
})
.then(response => {
this.setState({
partners: response.items
})
})
.catch(console.error);
}
render(){
console.log("SIDE BAR RESULT: ", this.state.partners)
console.log(this.state.partners[0])
return null;
}
Console.log("SIDE BAR RESULT: ", this.state.partners) will display all the results from the contentful cms. When use with array index example this.state.partners[0].fields error will appear
Cannot read property 'fields' of undefined
Does anyone know why with the array index it will cause error message ?
At initial state - this.state.partners is empty array so finding this.state.partners[0].fields will give error
you need to put check in render
if(this.state.partners.length > 0) {
this.state.partners[0].fields
}
Once promise is resolved it will set the state.partners in componentDidMount(), render() will update itself.
In the following SideMenuRow child div inside my SideMenuContainer component, I'm calling the changeInfoList method. The method is in the parent App.js.
In App.js:
<SideMenuContainer changeInfoList={this.changeInfoList} genInfoList={this.state.genInfoList} groupedObjectsList={this.state.groupedObjectsList}/>
In SideMenuContainer.js:
<SideMenuRow onClick={this.props.changeInfoList(genInfoElement.name)}>
Here are the relevant sections of the App.js code involving the method and state:
constructor(props) {
super(props);
this.state = {
genInfoList: [],
groupedObjectsList: [],
cardInfo: {
name: data[0].name
},
data: []
};
}
changeInfoList(rowName){
//change the card rendered based on which row is clicked
this.setState({
cardInfo: {
name: rowName
}
})
}
What's incorrect about the way I'm structuring this method that isn't allowing setState to work?
You're getting this error because the keyword this is not pointing to your component's execution context and it's current execution context does not have a method called setState()
You can approach this a couple of ways:
1) Define an arrow function like the following:
const changeInfoList = (rowName) => {
//change the card rendered based on which row is clicked
this.setState({
cardInfo: {
name: rowName
}
})
}
By using an arrow-function (non-binding this), the this keyword now points to the closest object, in this case your class component, which has a setState() method.
2) Bind your current function inside your component's constructor.
constructor(props) {
super(props);
this.state = {
genInfoList: [],
groupedObjectsList: [],
cardInfo: {
name: data[0].name
},
data: []
};
this.changeInfoList = this.changeInfoList.bind(this)
}
This explicitly tells your function that this is now pointed to your class component's context.
Now assuming you did either of these steps, you would then have to pass down changeInfoList into your child components:
In App.js:
<SideMenuContainer changeInfoList={this.changeInfoList} genInfoList={this.state.genInfoList} groupedObjectsList={this.state.groupedObjectsList}/>
In SideMenuContainer:
<SideMenuRow onClick={() => { this.props.changeInfoList(genInfoElement.name) }}>
constructor(props) {
super(props);
this.state = {
genInfoList: [],
groupedObjectsList: [],
cardInfo: {
name: data[0].name
},
data: []
};
// Bind the function
this.changeInfoList = this.changeInfoList.bind(this)
}
changeInfoList(rowName){
//change the card rendered based on which row is clicked
this.setState({
cardInfo: {
name: rowName
}
})
}
I don't know, why I can't setState my response data of REST API. Console.log just working. Where is problem? I can't find problem in my code.
class RecentTransactions extends Component {
constructor() {
super();
this.setState = {
recentTransactionsSender: [],
recentTransactionsRecipient: [],
};
}
componentDidMount() {
axios
.all([
axios.get('http://localhost:3000/api/transactions/recipient/1'),
axios.get('http://localhost:3000/api/transactions/sender/1'),
])
.then(
axios.spread(
(recentTransactionsRecipient, recentTransactionsSender) => {
this.setState({
recentTransactionsRecipient: recentTransactionsRecipient.data,
recentTransactionsSender: recentTransactionsSender.data,
});
},
),
)
.catch(err => {});
}
First and foremost you are setting the initial state incorrectly. You should be assigning the initial state to the state object rather than to setState.
this.state = {
recentTransactionsSender: [],
recentTransactionsRecipient: [],
};
Moreover, since you aren't doing anything in the constructor other than setting the initial state, you can remove it and instead do
class RecentTransactions extends Component {
state = {
recentTransactionsSender: [],
recentTransactionsRecipient: [],
};
}
Other than that, without having more details on what's not working it's impossible to help you any further.
The problem I face is that it doesn't seem that componentDidMount is re-rendering my component, even though it is updating the state. Lot of code coming up, but it gives context to the issue I'm having. If I need to, I can upload screenshots of what is happening.
Here's the constructor:
export class Upload extends React.Component<RouteComponentProps<{}>, UploadTaggingOptions> {
constructor(props: any) {
super(props);
this.state = {
photographer: [
{ id: null, value: '', label: '' },
],
};
}
Here's component did mount:
componentDidMount {
//Fetch request for photographers from the db
fetch("http://localhost:49775/api/photographers")
.then(res => res.json())
.then((result) => {
var photographerData = this.state!.photographer;
var y = 0;
//Remove the empty object first and foremost. The list should now be totally empty
photographerData.shift();
//The loop to add the galleries to the galleryData array
for (var i in result) {
var id = result[i].id;
var value = result[i].firstname + ' ' + result[i].lastname;
var label = value;
var photographer = { "id": id, "value": value, "label": label };
photographerData.push(photographer);
y++;
}
this.setState({
isLoaded: true,
photographer: photographerData
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
alert("Error loading options for the photographers. Refresh the page. If the error persists, please contact your administrator");
}
)
And finally Render:
public render() {
return <div>
<div className="photographers">
<p><b>Photographer:</b></p>
<DropDown options={this.state!.photographer} />
</div>
}
Just for clarity's sake, there are more components on the screen (hence the extra div for the dropdown component).
I'm not sure why, but the dropdown renders with the blank options I intitialize in the constructor, componentdidupdate does the fetch request AND updates the state to the data that was fetched, but I have to click the blank value in order to load those data values into the dropdown. It is almost like it re-renders after I change the selected value instead of on state change.
I've tried moving those requests into the constructor, but have the same problem. Perhaps
EDIT: Here's the code for the Dropdown component:
import * as React from 'react';
import Select from 'react-select';
const DropDown = (props: any) => {
return (
<div className="dropdown">
<Select
closeOnSelect={!(props.stayOpen)}
disabled={props.disabled}
options={props.options}
placeholder="Select an option..."
removeSelected={props.removeSelected}
simpleValue
value={props.selectedPhotographer}
searchable={true}
multi={true}
/>
</div>
)
}
export default DropDown;
From react official documentation:
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
But in your code you are mutating it, albeit via an assignment to another variable:
var photographerData = this.state!.photographer;
// this DOES mutate the original array.
photographerData.shift();
This can mess with Reacts batching update strategy and can cause delays.
If you do not need the data from original state, you can just do:
var photographerData = [];
window.onload = function() {
console.log('Testing');
let testArr1 = [1, 2, 3]
console.log('TestArr1 length before shift: ' + testArr1.length);
let testArr2 = testArr1;
testArr2.shift();
console.log('TestArr1 length after shift: ' + testArr1.length);
}
I can't seem to access data that's part of an object within an object. here I'm trying to access likes in profile which would otherwise be fine using vanilla javascript to print out this.state.data.profile.likes
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: {}
};
}
componentDidMount() {
var x = {
"notifications": 12,
"profile": {
"likes": 5
}
};
this.setState({
data: x
});
}
render() {
const {notifications, profile} = this.state;
return (
<div>
<span>Notifications {notifications}</span>
<span>Likes {profile.likes}</span>
</div>
);
}
Before mounting - and on the initial render - your state looks like this:
{
data: {}
}
After mounting - on the second render - your state looks like this:
{
data: {
notifications: 12,
profile: {
likes: 5
}
}
}
You're trying to access this.state.profile.likes which doesn't exist. Presumably you mean to access this.state.data.profile.likes which does exist, but only on the second render.
I noticed this while also trying to fix the same problem. The constructor should be:
constructor(props) {
super(props);
this.state = {
data: {
profile: {}
}
};
}
Always initialize objects within objects
https://reactjs.org/docs/react-component.html#mounting
render -> componentDidMount
put state data in constructor