javascriptreactjsstatesreact-props

Weather App with React, Prop not receiving and updating


With my weather app I am creating a system where you can write in a custom longitude and latitude, and once it receives both, passes to the child component using a prop. There, it will be added into the API call and pull data on that area.

Everything works when I put the long/lat in the API request manually, but not it won't receive the prop correctly. Currently every character I type in the test fields seem to case a chain reaction for both components. the prop the child is receiving is also undefined.

Any help would be greatly appreciated, as this is a learning experience for me.

The goal: Wait for both long and lat to be submitted, then added into API call.

The Code:

weatherView.js: where the long and lat is entered

import React, { Component } from 'react';
import { WeatherCard } from './weatherCard';

export class WeatherView extends Component {
    constructor(props) {
        super(props);
        this.state = {
            lat: " ",
            long: " ",
            valueLat: " ",
            valueLong: " ",
            latBool: false,
            longBool: false,
            latLong: " "
        }
    }

    onChangeLat = (e) => {
        this.setState({valueLat: e.target.value});
    }

    onChangeLong = (e) => {
        this.setState({valueLong: e.target.value});
        console.log('Value is' + this.state.valueLong )
    }

    onSubmitLat = (e) => {
        e.preventDefault()
        if (this.state.valueLat === " ") {
            alert("You must enter something");
        } else {
            this.setState({
                lat: this.state.valueLat,
                latBool: true
            })
            console.log(this.state.valueLat)
        }
    }

    onSubmitLong = (e) => {
        e.preventDefault()
        if (this.state.valueLong === " ") {
            alert("You must enter something");
        } else {
            this.setState({
                long: this.state.valueLong,
                longBool: true
            })
            console.log(this.state.valueLong)
        }
    }



    componentDidMount(){

        if(this.state.latBool === true && this.state.longBool === true) {
            this.setState({
                latLong: this.state.lat + "," + this.state.long
            })

        }
    }

    render() {
        return(
            <div>
                <h1>Welcome to the Weather App!</h1>
                <form onSubmit={this.onSubmitLat}>
                    Enter the Latitude in decimal format: <input type="text" value={this.state.valueLat} onChange={this.onChangeLat}/> 
                    <button >Submit</button>
                </form>
                <form onSubmit={this.onSubmitLong}>
                    Enter the Longitude in decimal format: <input type="text" value={this.state.valueLong} onChange={this.onChangeLong}/> 
                    <button>Submit</button>
                </form>
                <WeatherCard latLong = {this.state.latLong}/>
            </div>
        )
    }
}

weatherCard.js: the child where the API is called and everything is rendered.

import React, { Component } from 'react';
import ReactAnimatedWeather from 'react-animated-weather';

const defaults = [
{
    icon: 'CLEAR_DAY',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'CLEAR_NIGHT',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'PARTLY_CLOUDY_DAY',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'PARTLY_CLOUDY_NIGHT',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'CLOUDY',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'RAIN',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'SLEET',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'SNOW',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'WIND',
    color: 'white',
    size: 175,
    animate: true
},
{
    icon: 'FOG',
    color: 'white',
    size: 175,
    animate: true
}
];


function iconConverter(arg){
    switch (arg) {
        case 'clear-day': return 0;
            break;
        case 'clear-night': return 1;
            break;
        case 'partly-cloudy-day': return 2;
            break;
        case 'partly-cloudy-night': return 3;
            break;
        case 'cloudy': return 4;
            break;
        case 'rain': return 5;
            break;
        case 'sleet': return 6;
            break;
        case 'snow': return 7;
            break;
        case 'wind': return 8;
            break;
        case 'fog': return 9;
            break;

    }
}

const WCard = ({day, high, low, humidity, summary, sunrise, sunset, windspeed, time, rainProb, icon}) =>{
    return (
        <div>
            <p>{time}</p>
            <div id='wCardIcon'>

                <ReactAnimatedWeather

                    icon={defaults[iconConverter(icon)].icon}
                    color={defaults[iconConverter(icon)].color}
                    size={defaults[iconConverter(icon)].size}
                    animate={defaults[iconConverter(icon)].animate}
                  />
                <div>
                    <p>&#8679; {high}&#8457;</p>
                    <p>{low}&#8457; &#8681;</p>
                </div>
            </div>
            <p id="wCardSum">{summary}</p>
            <p>Humidity: {humidity}%</p>
            <p>Wind speed: {windspeed}mph</p>
            <p>Sunrise: {sunrise}</p>
            <p>Sunset: {sunset}</p>
            <p>Chance of rain: {rainProb}%</p>

        </div>
    )};



// const weatherAPI = 'https://api.darksky.net/forecast/926bb6de03f1ae8575d48aaeb2fc9b83/34.0522,-118.2437';

const weatherAPI = 'https://api.darksky.net/forecast/926bb6de03f1ae8575d48aaeb2fc9b83/';

export class WeatherCard extends Component {
    constructor(props) {
        super(props)
        this.state = {
            requestFailed: false,
            info: '',
            latLongSubmitted: false,
            latLongValue: this.props.latLong,
            weatherAPI: 'https://api.darksky.net/forecast/926bb6de03f1ae8575d48aaeb2fc9b83/'
        }
    }   



    componentWillReceiveProps(nextProps){

        console.log("Receive Props activated")
        console.log("Prop: " + this.props.latLong)
        console.log("Value for API" + this.latLongValue)

        if(this.props.latLong !== nextProps.latLong) {
            this.setState({
                latLongValue: nextProps.latLong,
                latLongSubmitted: true
            })      
            console.log(this.latLongValue)
        }
    }

    shouldComponentUpdate(nextProps) {
        console.log('shouldComponentUpdate activated');
    return this.state.latLongValue !== nextProps.latLongValue;
}

    timeDateConverter(tempTime) {
        var time = tempTime *1000;
        var d = new Date(time);
        var formattedDate = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();

        return formattedDate
    }

    removeMilitary(hours){ 

        if (hours > 0 && hours <= 12) {
            hours = "" + hours;
        } else if (hours > 12) {
            hours = "" + (hours - 12);
        } else if (hours === 0) {
            hours= "12";
        }
        return hours;
    }

    timeConverter(tempTime) {
        var time = tempTime *1000;
        var d = new Date(time);
        var hours = d.getHours();
        if (hours>=12){                 //Adding endings
                var suffix = "P.M.";}
            else{
                suffix = "A.M.";}
        var minutes = (d.getMinutes() < 10) ? "0" + d.getMinutes() : d.getMinutes();

        hours = this.removeMilitary(hours);

        var formattedTime = hours + ":" + minutes + " " + suffix;

        return formattedTime;
    }

    componentDidMount() {
        if (this.state.latLongSubmitted)
            console.log('componentDidMount is running')
            fetch(this.state.weatherAPI + this.state.latLongValue)
            .then(response => {
                if (!response.ok) {
                    throw Error("Network request failed")
                }
                return response;
            })
            .then(data => data.json())
            .then(data => {
                this.setState({
                    info: data
                })
                console.log(data)
            }, () => {
                this.setState({
                requestFailed: true
                })
            })
    }


    render() {
        if (!this.state.latLongSubmitted) return <p>Waiting for coordinates...</p>
        if (this.state.requestFailed) return <p>Failed</p>
        if (!this.state.info) return <p>Loading...</p>
        return(
            <div>
                <h1>The current temperature in {this.state.info.timezone} is: {this.state.info.currently.apparentTemperature}&#8457;.</h1>
                <h1>The 8 day forecast for {this.state.info.timezone}:</h1>
                <ul>
                    {this.state.info.daily.data.map((day, id) => 
                        <div key={{id}>{day}} id="weatherCard">
                            <WCard time={this.timeDateConverter(day.time)}
                                high={day.temperatureHigh}
                                low={day.temperatureLow}
                                summary={day.summary}
                                icon={day.icon}
                                humidity={day.humidity}
                                sunrise={this.timeConverter(day.sunriseTime)}
                                sunset={this.timeConverter(day.sunsetTime)}
                                rainProb={day.precipProbability}
                                windspeed={day.windSpeed}
                            />
                        </div>
                    )}
                </ul>

                <a href="https://darksky.net/poweredby/">Powered by DarkSky</a>
            </div>
        )
    }
}

Solution

  • Its due to Lattitude and longitude is taken independently.componentWillRecieveProps() is called only when property changes. In your case latLong variable is changed only first time component loads(componentDidMount()). Try like this inside componentWillUpdate()

      componentWillUpdate(){
    
        if(this.state.latBool === true && this.state.longBool === true) {
            this.setState({
                latLong: this.state.lat + "," + this.state.long
            })
    
        }
    }