Ivcota
Ivcota

Reputation: 37

How do I pull a nested state object without it returning as "undefined"

I continue to struggle with what I believe would be something simple to accomplish. The first time I tried this I used React Hooks, but this time I switched back to class based components just because I thought it would change something for some reason.

import React, { Component } from 'react'
import Search from './components/layout/Search'
import axios from 'axios'

class App extends Component {

  state = {
    weather: {},
    loading: false
  }
  


  sendUp = async (state) => {
   this.setState({loading: true});
   const res = await axios.get(`http://api.openweathermap.org/data/2.5/weather?q=${state}&appid={api key hidden for post.}`)
   this.setState({weather: res.data})
   this.setState({loading: false})
  }

  render() {
    return (
      <React.Fragment>
      <div className="container">
        <Search sendUp={this.sendUp} />
        <h1>Testing this stuff {this.state.weather.coord.lat} </h1>
      </div>
    </React.Fragment>
    )
  }
}

export default App


Hopefully my confusion makes sense. I thought it would be as simple as pulling the date like this: {this.state.weather.coord.lat}. However, I get this error. Cannot read property 'lat' of undefined. Did I just miss some sort of basic javascript fundamental or is calling nested objects different when using the React Library?

{
  "weather": {
    "coord": "{lat: 32.88, lon: -111.76}",
    "weather": "[{…}]",
    "base": "stations",
    "main": "{feels_like: 311.12, humidity: 32, pressure: 1012, …}",
    "visibility": 10000,
    "wind": "{deg: 330, speed: 3.6}",
    "clouds": "{all: 1}",
    "dt": 1595714117,
    "sys": "{country: \"US\", id: 3616, sunrise: 1595680572, suns…}",
    "timezone": -25200,
    "id": 5288636,
    "name": "Casa Grande",
    "cod": 200
  },
  "loading": false
}

Upvotes: 0

Views: 197

Answers (2)

Giovanni Esposito
Giovanni Esposito

Reputation: 11156

Ciao, you got undefined because this.state.weather contains an element called weather so if you want to access to your object you have to write this.state.weather.weather. But there is another problem: if you want to get lat from coord you cannot write this.state.weather.weather.coord.lat because this.state.weather.weather.coord is a string, not a JSON object. And you cannot do neither something like JSON.parse(this.state.weather.weather.coord) because is not a valid JSON. So, we have to work with string and, finally, to get lat from coord you can do:

this.state.weather.weather.coord.split(',')[0].split(':')[1]

Upvotes: 2

Gonzalo.-
Gonzalo.-

Reputation: 12672

the render function is executed at the very beginning of your component being mounted. Everytime your state changes, the render function will be executed again. There are other situations where the component might render (for example, props changing).

The first time your component renders, your state is

state = {
    weather: {},
    loading: false
  }

but in your render function, you do

 <h1>Testing this stuff {this.state.weather.coord.lat} </h1>

This is because at this point, you haven't loaded the weather info yet. Your render component should consider that. For instance, you could do something like

{ this.state.weather.coord ? <h1>Testing this stuff {this.state.weather.coord.lat}</h1> : <h1>Loading weather data...</h1>}

That way, until your weather data is loaded, you see the text "Loading...". You could also do something more sophisticated, like showing a spinner.

Upvotes: 1

Related Questions