How can I prevent the state element from being passed to the child component before it is set?


Noiseymur

I'm writing a weather forecast app using React. I'm getting the data from the openweathermap.org API. But to use that I need to know the location of the user. So I also use other APIs one by one to identify user IP, location and then weather data according to that location. At each retrieval state, I update the initial states with retrieved information. For example, when getting Ip, I update userIP in states with setState, then when getting latitude and longitude, I also update userLat and userLng. Therefore, in the states, WeatherData, which is initially an empty array, will be updated last.The problem is that rendering is performed every time one of the states is changed. Because one of the child components that I'm passing the weatherData to as props uses an object in that fetched weatherData array, I get an error because rendering is in progress and passing an empty array to that component until the WeatherData is updated. I've tried using the if statement to check if WeatherData is an empty array or not before returning results, but somehow it doesn't work.

Here is my App.js file:

import React, {Component} from 'react';
import './App.css';
import Mainblock from './Mainblock';
import Hourly from './Hourly';
import Weekly from './Weekly';

class App extends Component {
  constructor() {
    super()

    this.state = {
      userIp: 0,
      cityName: '',
      cityNameNoSpace: '',
      userLat: 0,
      userLng: 0
    }

  }
  

  componentDidMount(){

    fetch("https://geoip-db.com/json/").then((data)=> {

      return data.json();
    }).then((ip)=>{

      this.setState({userIp: ip.IPv4});
      this.locateClient(this.state.userIp);
    });
  }

  locateClient = (clientIp) => {
    fetch(`https://ip-geolocation.whoisxmlapi.com/api/v1?apiKey=at_SECRETAPIKEY&ipAddress=${clientIp}`).then((data)=>{

      return data.json();
    }).then((locationInfo)=>{

      this.setState({userLat: locationInfo.location.lat, userLng: locationInfo.location.lng, cityName: locationInfo.location.city});

      let cityArray = Array.from(locationInfo.location.city);

      let cityNameFiltered = '';
      
      cityArray.forEach((letter)=>{
        cityNameFiltered = cityNameFiltered + letter;
        return cityNameFiltered;
      })

      this.setState({cityNameNoSpace: cityNameFiltered});

      this.getWeatherData(this.state.cityNameNoSpace);

    });
  }

  getWeatherData = (userCity) => {

    fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${this.state.userLat}&lon=${this.state.userLng}&units=metric&appid=SECRETAPIKEY`).then((data)=>{

      return data.json();
    }).then((weatherInfo)=>{

      this.setState({weatherData: weatherInfo});
    });
  }

  

  render() {

    return (
      <div className="whole-container">
        <div className="lside">
          <Mainblock states={this.state}/>
          <Weekly />
        </div> 
        <Hourly />
      </div>
    );
    
  }
}

export default App;
Kyle

Since your Mainblockcomponent expects the state prop to be an object with the weatherData property, where weatherData should be an array, you can conditionally render the component .

To render it conditionally, it would look like this:

render() {
    return (
      <div className="whole-container">
        <div className="lside">
          {Array.isArray(this.state.weatherData) && <Mainblock states={this.state}/> }
          <Weekly />
        </div> 
        <Hourly />
      </div>
    );
  }

The reason this works is because javascript evaluates boolean expressions and returns the right side of the expression if true, else it returns false.

> true && 123
< 123
> false && 123
< false

Related


Can I set state in parent component of child component?

Eliot I want to clean up my code a bit, so I want to create subcomponents instead of one long component. In Parentsome of my states, I want to be onClickin Child. Parents: const [plan, setPlan] = useState('myPlan'); const [months, setMonths] = useState('2'); c