使用 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>⇧ {high}℉</p>
<p>{low}℉ ⇩</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}℉.</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。
我已经成功构建了显示洛杉矶八天天气预报的应用程序。我的下一个任务是更改 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>⇧ {high}℉</p>
<p>{low}℉ ⇩</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}℉.</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。