我正试图使用一个api创建一个简单的天气web应用程序,该应用程序包含一个搜索城市和显示信息的输入表单。我想使用useEffect挂钩将默认城市设置为特定城市。因此,用户一打开应用程序,就会显示有关该市天气的信息。但是,它会返回一个错误,表示不存在类似的数据。
这是我的App.js文件
import React, { useState, useEffect } from 'react'
import Search from './components/Search.js'
import './App.scss';
function App() {
const [city, setCity] = useState('London')
const [unit, setUnit] = useState('metric')
const [info, setInfo] = useState({})
const key = process.env.REACT_APP_WEATHER_API_KEY
const api = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}&units=${unit}`
useEffect(() => {
fetch(api)
.then(res => {
if (!res.ok){
throw Error('could not fetch the data')
}
return res.json()
})
.then(data => {
setInfo(data)
console.log(data)
})
.catch(err => console.log(err.message))
}, [city])
return (
<div className="app">
<Search setCity={setCity}/>
<h1 className='city'>{city}</h1>
<h2 className='temperature'>Temperature: {info.main.temp}</h2>
</div>
);
}
export default App;
Search.js文件
import React from 'react'
function Search({setCity}) {
const search = (e) => {
const loc = e.target.value.trim().replace(/^w/, (c) => c.toUpperCase())
if (e.key === 'Enter' && loc !== ''){
setCity(loc)
}
}
return (
<div className='search-container'>
<input type="text" onKeyPress={search} defaultValue='London'/>
</div>
)
}
export default Search
错误消息
Uncaught TypeError: Cannot read properties of undefined (reading 'temp')
这个错误似乎是因为我试图访问h2标签中的温度信息(info.main.temp(而发生的。但是,如果我删除它,一切都会正常工作,并且信息仍然会从api中检索到。
您已将info
初始化为空对象-
const [info, setInfo] = useState({})
组件第一次渲染时,info
对象上没有main
属性(因为它是空的(,所以此调用总是失败,并显示错误:
<h2 className='temperature'>Temperature: {info.main.temp}</h2>
要修复它,您可以在使用可选链接访问之前检查属性是否存在:
<h2 className='temperature'>Temperature: {info.main?.temp}</h2>
您的使用效果取决于"城市";。一开始,你试图在任何城市变化之前显示温度值。
为了解决这个问题,你可以避免尝试显示标签:
{info && <h2 className='temperature'>Temperature: {info.main.temp}</h2>}
或:
{info ? <h2 className='temperature'>Temperature: {info.main.temp}</h2> : <Loading/>}
在这种情况下,您不需要将默认值设置为"0";信息";useState。
const [info, setInfo] = useState()
如果您检查info.main
没有未定义,然后将其渲染出来,那将是更好的
import React, { useState, useEffect } from 'react'
import Search from './components/Search.js'
import './App.scss';
function App() {
const [city, setCity] = useState('London')
const [unit, setUnit] = useState('metric')
const [info, setInfo] = useState({})
const key = process.env.REACT_APP_WEATHER_API_KEY
const api = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}&units=${unit}`
useEffect(() => {
fetch(api)
.then(res => {
if (!res.ok){
throw Error('could not fetch the data')
}
return res.json()
})
.then(data => {
setInfo(data)
console.log(data)
})
.catch(err => console.log(err.message))
}, [city])
return (
<div className="app">
<Search setCity={setCity}/>
<h1 className='city'>{city}</h1>
{info.main ? (
<h2 className='temperature'>Temperature: {info.main.temp}</h2>
) : (
<h2 className='loading'>Loading...</h2>
)}
</div>
);
}
export default App;
您可以为weather API创建一个接口,如下所示。
export interface Coord {
lon: number;
lat: number;
}
export interface Weather {
id: number;
main: string;
description: string;
icon: string;
}
export interface Main {
temp: number;
feels_like: number;
temp_min: number;
temp_max: number;
pressure: number;
humidity: number;
}
export interface Wind {
speed: number;
deg: number;
}
export interface Clouds {
all: number;
}
export interface Sys {
type: number;
id: number;
message: number;
country: string;
sunrise: number;
sunset: number;
}
export interface RootObject {
coord: Coord;
weather: Weather[];
base: string;
main: Main;
visibility: number;
wind: Wind;
clouds: Clouds;
dt: number;
sys: Sys;
timezone: number;
id: number;
name: string;
cod: number;
}
export interface IWeather {
coord: Coord;
weather: Weather;
base: string;
main: Main;
visibility: number;
wind: Wind;
clouds: Clouds;
dt: number;
sys: Sys;
timezone: number;
id: number;
name: string;
cod: number;
}
然后在App.tsx中,您需要修改两件事。
const [info, setInfo] = useState<IWeather | null>();
和
<h2 className="temperature">Temperature: {info?.main?.temp}</h2>
你可以查看工作演示
如果你有任何疑问,请告诉我。
您收到该错误只是因为您试图在从api 获取数据之前访问临时数据
只需添加空检查"在信息之后,无论何时尝试从信息访问数据,一旦从api 获得数据,它都会检查临时数据
并在声明时将信息状态声明为null
const [info, setInfo] = useState(null)
<h2 className='temperature'>Temperature: {info?.main.temp}</h2>