使用 React 的天气应用程序:将自定义 Lat/Long 输入到 API 请求时出现问题

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

我已经成功构建了显示洛杉矶八天天气预报的应用程序。我的下一个任务是更改 API 请求的经度和纬度。 为此,我创建了两个输入字段,用户可以在其中输入 long/lat,然后我对其进行格式化并将其作为道具传递,以添加到 API 请求中。

主要可能是状态似乎未定义并且没有通过 prop。 当我在字段中键入时也会收到此警告:

我收到控制台错误

组件正在将文本类型的受控输入更改为不受控制。输入元素不应从受控切换到不受控(反之亦然)。在组件的生命周期内决定使用受控或非受控输入元素。

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>
        )
    }
}

这是我第一次用 React 做这么大的事情,所以非常感谢任何帮助。

问题出在您的 onChangeLat 和 "onChangeLong" 函数上。

更正如下。

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

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

尽管您可以使用 this.state.valueLat[ 访问 submit 函数中的受控输入值=58=] onChange 函数就不是这样了。

应该像下面这样访问

e.target.value

这种处理表单字段的技术被称为“受控组件

一个有用的 link 受控输入

回答你的第二个问题

1) WeatherCard 组件的 componentWillReceiveProps 有一个拼写错误。

2) 由于您传递给组件的初始道具为空,因此 fetch 调用失败。为此,您需要使用“componentWillReceiveProps”而不是“componentDidMount()”。

componentDidMount() 只被调用一次。我认为这会有所帮助。

这是我为此创建的 jsfiddle