Whenever I try to fetch my results from componentWillMount the application hits render() first it comes out and then it hits componentWillMount fetches my results, sets the state and then hits render again.
componentWillMount= () => {
let team = gh.getRepo('Microsoft', 'vscode');
team.getContributors(function(err, members){
}).then(response => {
this.setState({
data: response.data
});
});
}
render() {
var ghList = this.state.data;
const names = ghList.map(name => { //when it runs the first time this map in invalid since ghList is null from my this.setState= {data:null}
})
return (
<div className="flip-container" onClick={this.onTileClick}>
<div className="flipper">
<div className="front">
<img className="circle" src={this.state.avatar_url} alt={this.state.login}/>
</div>
<div className="back">
</div>
</div>
</div>
)
If I understood correctly, it's the expected behavior. What is happening:
componentWillMount(): triggers an async request (getContributors).
render() first time, with this.state.data = undefined.
callback from getContributors is called (the response has come), and, then, setState is invoked: setState schedules a new rendering.
render() second time, with populated this.state.data
You'll have to handle your initial state rendering (how your component will render before the ajax returns: maybe some loading spinner..). So, in the render method, you can check if this.state.data is null/undefined and bypass your logic. Or, in constructor, set your data to an empty array:
constructor(props) {
super(props);
this.state = {
data: []
}
}
This way, at least ghList.map will not thrown an error.
Related
I am creating a news app for my project and what i am basically doing is fetching the arrays of news articles from the API and then storing it in a articles array in my local memory and then I am using the map method on that array and thus rendering the articles on my web page but now I'm getting this error:
image of the error
I looked at some similar question I think what I am having a problem with the scope of the constructor.
EDIT: I was wrong at the setstate part but i was just trying it out but the original code is this:
this.state = {
articles: this.articles
}
the above code is the original code in the place of setstate that was there before the below code is the complete code
articles = [];
constructor() {
super();
console.log(typeof articles);
this.state = {
articles: this.articles
}
// let category = 'top-headlines';
// let country = 'in';
}
async componentDidMount() {
const YOUR_API_KEY = 'cant show you this';
// let q = '';
let url = `https://newsapi.org/v2/top-headlines?country=in&apiKey=${YOUR_API_KEY}`;
const FetchNews = async () => {
const response = await fetch(url);
const parsedjson = await response.json();
console.log(parsedjson);
this.setState({ articles: parsedjson.articles });
}
FetchNews();
}
render() {
return (
<div className="container p-1" style={{ backgroundColor: '#F0CE57' }}>
<div className="noogle">
<h1>Noogle</h1>
<input type="text" placeholder='Search News' />
</div>
<div className="row">
{this.state.articles.map((element) => {
return <div className="col-md-4" key={element.url}>
<NewsCard title={element.title} desc={element.description} imgurl={element.urlToImage} url={element.url} />
</div>
})}
</div>
</div>
)
}
}
Because this.state doesn't have a property called articles. It has a property called this which is set to... whatever this.setState returns while trying to set the state to this.articles, which also doesn't exist. This isn't at all what you want:
this.state = {
this: this.setState({ articles: this.articles })
}
I don't know where you pieced together that example, but it's all wrong. Just initialize the state value to an empty array:
this.state = {
articles: []
}
Then on initial render you simply have no records to display. And when the AJAX operation completes and sets the new state value you'll have records on the next re-render.
You don't need (or want) your global articles variable. It's going to tempt you to mutate values directly and cause strange bugs.
Why are you setting the state inside constructor. Instead when you are calling this.setState() function , it will set the state of articles
Try this code
articles = [];
constructor() {
super();
console.log(typeof articles);
this.state = {
articles : []
}
// let category = 'top-headlines';
// let country = 'in';
}
tell me, please, how to solve the following problem correctly?
I have a certain component, there is a control above, when I click on it, setState is triggered. I need to call the function this.setScrollLeft () in which I set to the selected node (ref) in this case the cleavage position.
Here is my implementation, but I am sure that there is a better solution:
import React from 'react';
import { ScoreCell, getScoreTheme } from 'components/scores';
class LeaderboardPlayerResult extends React.Component {
constructor(props) {
super(props);
this.containerWidth = 198;
this.data = this.props.data;
this.playerResultRef = React.createRef();
}
componentDidMount() {
this.element = this.playerResultRef.current;
this.element.scrollLeft = this.containerWidth;
}
setScrollLeft = () => {
if (this.element) {
this.element.scrollLeft = this.containerWidth;
}
};
playerResult = () => {
if (this.data.playOffHoles) {
return (
this.data.playOffHoles.map((item, index) => {
return (
<div
className="leaderboard__player-result-row-wrapper"
key={index}
>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holeId}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holePar}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell leaderboard__player-result-cell--score">
<ScoreCell
childCss='tee-times-card__score'
theme={getScoreTheme(item.playOffParScore)}
>{item.playOffParScore}</ScoreCell>
</div>
</div>
</div>
);
})
);
}
};
render() {
console.log('LeaderboardPlayerResult render');
this.setScrollLeft();
return (
<div
className="leaderboard__player-result"
ref={this.playerResultRef}
>
{this.playerResult()}
</div>
);
}
}
The best place to put this.setScrollLeft() is inside the componentDidUpdate method.
You are already calling this method (this.setScrollLeft()) inside componentDidMount, what is right. Now, you could put another call into componentDidUpdate and it will work pretty much as it is working by now because componentDidUpdate is called before render.
The final outcome will be the same, however, you are separating the concerns: render only render the components and the other methods deal with your business logic.
If you are not sure about componentDidMount and componentDidUpdate, see these excerpts from the official React.js documentation:
componentDidMount()
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Im new to react. I am trying to pull data from an API, and then loop through it and display it.
Error : Cannot read property 'map' of undefined.
The API data is coming through, but it seems as if React is calling the looplistings before the data is stored into State.
constructor () {
super()
this.state = {
data:'',
}
}
componentWillMount(){
// Im using axios here to get the info, confirmed data coming in.
//Updating 'data' state to equal the response data from the api call.
}
loopListings = () => {
return this.state.data.hits.map((item, i) => {
return(<div className="item-container" key={i}>
<div className="item-image"></div>
<div className="item-details">tssss</div>
</div>)
})
}
loopListings = () => {
return this.state.data.hits.map((item, i) => {
return(
<div className="item-container" key={i}>
<div className="item-image"></div>
<div className="item-details">tssss</div>
</div>)
})
}
render () {
return (
<div>
{this.loopListings()}
</div>
)
}
The reason you are receiving this error is that your call to the API is happening asynchronously to the react lifecycle methods. By the time the API response returned and persisted into the state the render method has been called for the first time and failed due to the fact you were trying to access an attribute on a yet undefined object.
In order to solve this, you need to make sure that until the API response has been persisted into the state the render method will not try to access that part of the state in your render method or to make sure that if it does there is a valid default state in the constructor:
Solve this by changing your render to do something like this:
render () {
return (
<div>
{this.state.data &&
Array.isArray(this.state.data.hits)
&& this.loopListings()}
</div>
)
}
or initialize your constructor like so :
constructor () {
super()
this.state = {
data: {hits: []},
}
}
Remeber react is just javascript and its behavior is just the same.
You could check if desir data.hits exists inside state.
{this.state.data && Array.isArray(this.state.data.hits) ?
this.loopListings()
: null}
Also make sure that, after retrieving a data cal this.setState method like below.
this.setState({ data })
I am brand new to Reacjs, I visited several question with a similar title but none helped me.
Why this code doesn't change my state?
componentWillMount()
{
/** I am using superagent to fetch some data but it doesn't matter */
var url = "http://www.omdbapi.com/?s=star&apikey=mykey";
Request.get(url).then((response) => {
this.setState({ messages: response.body.Search});
});
}
My render method
render() {
return (
<div>
<ListaMensagens messages={this.state.messages} /> this.state is null here.
</div>
...
How can I change my state with the retrieved data and pass it to a child component?
In general you can use setState in componentWillMount without getting into trouble, BUT...
in your case you are setting the state after the response of the request which is causing problems.
Three solutions for your problem:
1: Use the constructor to initialize the state.
constructor(props){
super(props);
this.state = {
messages: []
};
}
2: Fire setState in componentWillMount without waiting for any Promise
componentWillMount() {
this.setState({
messages: []
});
request(...);
}
3: In the render function check if the state is set properly
render(){
<div>
{ this.state && this.state.messages && <ListMensagens ... /> }
</div>
}
Is it possible to return a function inside the .then callback?
I want to do something like this:
axios.post('url_api/endpoint')
.then(function(response){
renderAdmissionReponse(){
<div>Success response.data</div>
}
})
To later render it in a component like this: "
<div className="row spacer">
{this.renderAdmissionReponse()}
</div>
Is it possible to do it?
I can actually understand your need, however, your above code is wrong!
What you want is: after data are loaded successfully by using axios, the data will be rendered properly on page. If so, you can use the following way:
constructor(props) {
// ...
this.state = ({
admissionResponse: [],
// your other state variables here
})
}
renderAdmissionReponse = (data) => {
this.setState({
admissionResponse: data,
});
}
// ... You can put this inside `ComponentDidMount()`, then just call the function to setState with returned data
axios.post('url_api/endpoint')
.then((response) => {
this.renderAdmissionReponse(response.data);
})
// ...
render() {
return (
<div className="row spacer">
<div>{this.state.admissionResponse.authorizationId}</div>
<div>{this.state.admissionResponse.authorizationNum}</div>
{this.state.admissionResponse.lineItems.map((line) =>{
return(
<div>{line.id}</div>
<div>{line.lineItemNumber}</div>
)
})}
</div>
);
}
Please also check your response.data structure then retrieve the correct keys, the above code I just try to explain about correct react's way on how to load data asynchronously and render on page after that. Please post here some errors if any, thanks.