Nicholas Hrboka
Nicholas Hrboka

Reputation: 177

Weather App using React: problems inputing custom Lat/Long into API request

I have successful built the app to show the weather forecast for eight days in Los Angeles. My next task is to change the longitude and latitude of the API request. To do this I created two input fields where the user can input long/lat, then I format it and pass it along as a prop to be added into the API request.

Main probably is it seems the states are undefined and isn't passing the prop. Also getting this warning when I type into a field:

Console error I'm getting

A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

weatherView.js:

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.valueLat});
    }

    onChangeLong = (e) => {
        this.setState({valueLong: e.target.valueLong});
        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:

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: " ",
            weatherAPI: 'https://api.darksky.net/forecast/926bb6de03f1ae8575d48aaeb2fc9b83/'
        }
    }

    coponentWillReceiveProps(nextProps){
        if(this.props.latLong !== nextProps.latLong) {
            this.setState({
                latLongValue: nextProps.latLong,
                latLongSubmitted: true
            })      
            console.log(this.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)
            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.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>
        )
    }
}

It's my first time doing something this big with React, so any help would be much appreciated.

Upvotes: 0

Views: 287

Answers (1)

Shashith Darshana
Shashith Darshana

Reputation: 2795

The problem is with your onChangeLat and "onChangeLong" functions.

Those should be corrected as below.

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

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

Though you can access the controlled input value inside the submit function with this.state.valueLat or this.state.valueLong it is not the case when it comes to the onChange functions.

The should be accessed like below

e.target.value

This technique of handling form field is known as "controlled component"

An useful link on controlled input

To answer your second question

1) WeatherCard component's componentWillReceiveProps had a spelling mistake.

2) And since the initial props you are passing to the component is empty the fetch call is failing. and for that you need to use "componentWillReceiveProps" instead of "componentDidMount()".

componentDidMount() get called only once. I think that will help.

Here is a jsfiddle I created for this.

Upvotes: 2

Related Questions