Hey Guys, this is my first post on Stack Overflow, but I will do my best to adhere to the proper formats! To sum it up, I have spent the last five hours on this react component, trying to render a list of objects being passed down from the state of my parent component, through the child's props. I have tried every which way, researched multiple solutions, but nothing is seeming to work. This is my first time using webpack, maybe there is some plugin I have to download for using React state and props? I dunno. Anyways, I will post snippets below and describe each step to the best of my ability.
My Parent App
I have had no trouble on this end, I have logged my results every step of the way so I know for a fact that the getWeather function is properly fetching data from the Weatherbit API and setting the state, which in turn as you will see, passes the array to the child via it's props.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {weatherArray: []}
this.getWeather = this.getWeather.bind(this);
}
getWeather() {
const newWeather = WeatherHelper.inputPrompt();
console.log(newWeather)
this.setState({weatherArray: newWeather})
WeatherHelper.showData()
}
Still on My Parent App
As you can see, I pass the state as a prop to the child, which would update soon as setState runs
<div id='side-apps'>
<div id='weather-side-app'>
<WeatherApp weatherArray={this.state.weatherArray} />
</div>
</div>
My Child App
So this is where it gets weird... This is my child component, and as you can see when the component receives new props, it first checks whether or not those props are updated. Then, assuming those props are new, the componentWillReceiveProps method will set the state and re-render accordingly (or so you would think). You can also see that I have extensively used logging to try to document my problem, which I will provide a screen shot of next. Notice that in the componentWillReceiveProps method, if the props are different than the originals (i.e. updated) the method will log multiple dashes around the updated props so you can point them out (you'll see). By the way, I understand that I shouldn't even have to use the componentWillReceiveProps method, the parents setState function alone should have been enough to update my component, but alas it did not (and neither did the other 10 different ways I tried).
class WeatherApp extends React.Component {
constructor(props) {
super(props);
this.state = {currentWeather: []}
}
handleCloseClick() {
WeatherHelper.hideData();
}
componentWillReceiveProps(nextProps) {
if (nextProps.weatherArray !== this.state.currentWeather) {
console.log('---')
console.log(nextProps.weatherArray)
console.log('---')
this.setState({currentWeather: nextProps.weatherArray})
} else {
{console.log("if you see an array under this comment," +
"that means the above if statement read the state's
currentWeather array's length" +
" as zero. EVEN THOUGH the componentWillReceiveProps method" +
" updated and set the state accordingly...")}
console.log(nextProps.weatherArray)
console.log(this.state.currentWeather)
}
}
render() {
if (this.state.currentWeather.length > 0) {
return (
<div class='weatherApp-main'>
<div className='close-sidebar-button'>
<img src='https://i.postimg.cc/hj0rYKxc/close-button.png' onClick={this.handleCloseClick}/>
</div>
<div id='weather-app-header'>
<h2>{this.state.currentWeather}</h2>
<h3></h3>
<h4></h4>
</div>
<div id='weather-app-table'>
</div>
</div>
)
} else {
{console.log(this.state.currentWeather)}
return <p></p>
}
}
}
Child Component Logging Details
if you look the the screen shot, you can see that my logging from my child's componentWillReceiveProps method proves that the setState function must have been called, but for some reason it does not update the child's html and display data.
child component logging details
Child Component React Details
Okay last thing... I also am posting the react chrome dev tool that shows you what components have props and states. You can see in this image, that the child component did receive the intended props and updated it's state accordingly. But again, for some reason it will not allow me to access them and display results
I really apologize if this question is too lengthy... And yes, I understand that there are multiple similar posts already solved, but I have looked at most of them, and my situation just does not apply from what I can tell... I am not an expert, but I am no newbie to react. This has really stumped me. Thank you so much in advance for your help. I can provide additional screenshots, code snippets, or even videos if that helps!!!
makezi
EDIT
I added replace the block of code with a map function, and as you can see, the problem is not how I am delivering the data to the DOM. The problem is for some reason, my component is not reading the data in it's state (I will post a more detailed screenshot to better document the problem)
render() {
console.log('++++')
console.log(this.state.currentWeather.length);
if (this.state.currentWeather.length > 0) {
return (
<div class='weatherApp-main'>
<div className='close-sidebar-button'>
<img src='https://i.postimg.cc/hj0rYKxc/close-button.png' onClick={this.handleCloseClick}/>
</div>
<div id='weather-app-header'>
{this.state.currentWeather.map(item => <div><span>{item.city}</span></div>)}
<h3></h3>
<h4></h4>
</div>
<div id='weather-app-table'>
</div>
</div>
)
} else {
{console.log("if you see an array under this comment," +
"that means the above if statement read the state's currentWeather array's length" +
" as zero. EVEN THOUGH the componentWillReceiveProps method" +
" updated and set the state accordingly...")}
{console.log(this.state.currentWeather)}
return <p></p>
}
}
Second Edit for Minh Hưng Trần's Comment
I updated my parent's getWeather function to be asynchronous, also edited logging to be more descriptive. You can see even though the currentWeather array in state has 8 objects, the length is still being read as zero! I don't think the problem is comparing the arrays. For some reason, my render function cannot read the data in my state... or it is not being updated when setState is ran. **you cannot tell from the screenshot, but the nextProps array is read and fully logged to console. although after running setState, the ''console.log(this.state.currentWeather)'' logged an empty array, weird right? **
componentWillReceiveProps(nextProps) {
if (nextProps.weatherArray !== this.state.currentWeather) {
console.log("--nextProps weatherArray--")
console.log(nextProps.weatherArray)
console.log('--------------------------')
this.setState({currentWeather: nextProps.weatherArray})
console.log("==child's state==")
console.log(this.state.currentWeather)
console.log("=================")
} else {
console.log(nextProps.weatherArray)
console.log(this.state.currentWeather)
}
}
render() {
console.log("++child's state's length in render method++")
console.log(this.state.currentWeather.length);
console.log("+++++++++++++++++++++++++++++++++++++++++++")
if (this.state.currentWeather.length > 0) {
return (
<div class='weatherApp-main'>
<div className='close-sidebar-button'>
<img src='https://i.postimg.cc/hj0rYKxc/close-button.png' onClick={this.handleCloseClick}/>
</div>
<div id='weather-app-header'>
{this.state.currentWeather.map(item => <div><span>{item.city}</span></div>)}
<h3></h3>
<h4></h4>
</div>
<div id='weather-app-table'>
</div>
</div>
)
} else {
{console.log("if you see an array under this comment," +
"that means the above if statement read the state's currentWeather array's length" +
" as zero. EVEN THOUGH the componentWillReceiveProps method" +
" updated and set the state accordingly...")}
{console.log(this.state.currentWeather)}
return <p></p>
}
}
To reiterate, in my componentwillreceiveprops function: the "console.log('nextProps.weatherArray)" DOES emit the full array of data... HOWEVER, the "console.log('this.state.currentWeather)" does NOT. Even after setState is run. But then when I use the React Chrome Dev Tools, you can see that the state DID in face get passed to the child component. My render method just cannot read the data for some reason..
THIRD EDIT
This first snippet is my parent component. You can see that it is passing the weatherArray to the child's props. This getWeather function DOES work by the way, I have been validating it through logging. The problem is on my child's end,I am sure of it.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {weatherArray: []}
this.getWeather = this.getWeather.bind(this);
}
async getWeather() {
const newWeather = await WeatherHelper.inputPrompt();
console.log(newWeather)
this.setState({weatherArray: newWeather})
WeatherHelper.showData()
}
render() {
return (
<div>
<div id='main'>
<div id='introduction'>
<Intro />
</div>
<div id='attachment'>
<Attachment getWeather={this.getWeather} />
</div>
<div id='body'>
<div id='about'>
<About />
</div>
<div id='personal-info-skills'>
<div id='info'>
<p id='info-header'>personal information</p>
<Info />
</div>
<div id='pskills'>
<p id='pskills-header'>personal skills</p>
<PSkills />
</div>
</div>
<div className='divider'>
<p></p>
</div>
<div id='professions-languages'>
<div id='professions'>
<p id='professions-header'>professions</p>
<Professions />
</div>
<div id='languages'>
<p id='languages-header'>languages</p>
<Languages />
</div>
</div>
</div>
</div>
<div id='side-apps'>
<div id='weather-side-app'>
<WeatherApp weatherArray={this.state.weatherArray} />
</div>
</div>
</div>
)
}
}
Now I will post my child's code snippet after commenting out componentWillReceiveProps. Instead of trying to display data in my child's state, now I am simply trying to use props data that was passed from the child's parent component. Unfortunately, I am still running into the same problem. My child's render function is reading the props array as empty, and therefore cannot display data. I can't take away that if statement at the beginning of the render method because then my code throws errors when it tries to read attributes of an empty array
render() {
if (this.props.weatherArray.length > 0) {
return (
<div class='weatherApp-main'>
<div className='close-sidebar-button'>
<img src='https://i.postimg.cc/hj0rYKxc/close-button.png' onClick={this.handleCloseClick}/>
</div>
<div id='weather-app-header'>
{this.props.weatherArray.map(item => <div><span>{item.city}</span></div>)}
<h3></h3>
<h4></h4>
</div>
<div id='weather-app-table'>
</div>
</div>
)
} else {
{console.log("if you see an array under this comment," +
"that means the above if statement read the state's currentWeather array's length" +
" as zero. EVEN THOUGH the React Chrome Dev Tools shows that" +
" the child has a full currentWeather array in props...")}
{console.log(this.state.currentWeather)}
return <p></p>
}
}
}
before running getWeather function screen shot
after running getWeather function screen shot
Look at the two above screenshots. The first one is before I run my fetch function, so the React Chrome Dev Tools show my child's props array is empty (of course). BUT the after screen shot shows that even though the React Dev Tools show my child component's props has a full array, my render method still reads that prop array's length as 0....