构建一个简单的 React Weather 应用
介绍
调用第三方 API 并使用返回的数据来驱动 React 视图是 Web 开发人员需要多次做的事情。
本指南将介绍如何构建一个 React 应用,向用户显示世界各地的当前天气。本指南中使用的天气 API 是Open Weather Map API;但是,这些概念与使用任何 API 都同样相关。
该应用程序将允许用户添加面板、为每个面板输入位置并检索该位置的当前天气。可以添加任意数量的面板,位置将存储在浏览器localStorage中,并在应用程序重新加载时检索。可以在 [此处](https://github.com/ChrisDobby/react-simple-weather-app) 找到该应用程序的源代码,以及如何设置天气 API 的详细信息。该应用程序的行为如下:
检索数据
此应用程序最重要的部分是天气信息。要获取特定位置的天气,需要向天气 API 发送GET请求,然后该 API 会以 JSON 格式返回天气信息。执行此操作的函数如下所示:
async function getLocationWeather(location) {
const result = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=metric`);
return result.json();
}
可以像这样使用:
await getLocationWeather("London");
上述getLocationWeather实现非常简单,因为它假设 API 正在运行,输入的位置将始终被找到,并且 API 中不会出现任何错误。除了简单地从函数返回 json ()结果之外,您还可以检查调用fetch的状态并返回适当的结果:
const result = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=metric`);
if (result.status === 200) {
return { success: true, data: await result.json() };
}
return { success: false, error: result.statusText };
这将测试结果的status属性。 如果是200 (表示 OK 的 http 状态代码),则 API 调用成功,您可以返回对json()的调用结果。 但是,如果状态不是200,则 API 调用由于某种原因失败,并且statusText属性可以作为错误描述返回。 为了让此函数的使用者可以简单地识别调用是否成功,该函数返回一个success属性:如果success为true,则结果将具有data属性 - 即该位置的天气数据 - 如果success为false,则结果将具有error属性,即对错误的描述。 对于这个相当简单的应用程序,错误描述只是 statusText ,但在现实世界的应用程序中,它应该是更用户友好的描述。
最后,getLocationWeather函数应该将对fetch的调用包装在try...catch块中,以防抛出异常。如果捕获到异常,成功应该为false,错误描述将是异常消息文本,但如上所述,在实际应用中,这应该更加用户友好。该函数最终如下所示:
async function getLocationWeather(location) {
try {
const result = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=metric`);
if (result.status === 200) {
return { success: true, data: await result.json() };
}
return { success: false, error: result.statusText };
} catch (ex) {
return { success: false, error: ex.message };
}
}
然后getLocationWeather的消费者可以测试成功属性并适当地显示天气数据或错误。这将在下一节中处理。
查看数据
演示应用程序的 UI 是使用Material UI构建的,但无论使用此 UI 库、其他 UI 库还是根本不使用 UI 库,构建视图的概念都是相同的。
App组件是应用程序的根组件,它将组件状态中当前可见的位置存储为字符串数组。初始化状态时,将从localStorage中检索最后使用的位置列表并将其存储为状态,如下所示:
const [weatherLocations, setWeatherLocations] = React.useState(readFromLocalStorage());
readFromLocalStorage函数检查localStorage中是否有任何位置。如果有,则返回它们,如果没有,则返回一个空数组。
此组件还包括一个辅助函数updateLocations,它接受字符串数组作为参数,并将数组写入localStorage并设置天气位置的状态。只要对位置的任何更新都通过此函数,localStorage和状态就会保持同步:
const updateLocations = locations => {
setWeatherLocations(locations);
saveToLocalStorage(locations);
};
视图的结构如下所示:
<div>
<AppBar position="static">
...
</AppBar>
<Grid>
{weatherLocations.map((location, index) => (
<Grid key={location} xs={12} sm={6} md={4} lg={3} item>
<WeatherCard
location={location}
canDelete={!location || canAddOrRemove}
onDelete={removeAtIndex(index)}
onUpdate={updateAtIndex(index)}
/>
</Grid>
))}
</Grid>
<Fab
onClick={handleAddClick}
color="secondary"
disabled={!canAddOrRemove}
>
<AddIcon />
</Fab>
</div>
Fab组件是一个Material UI按钮,点击时会调用handleAddClick,它只是向状态setWeatherLocations([...weatherLocations, ""])添加一个空的位置字符串。
位置视图由WeatherCard组件负责。如果正在查看的位置prop 是空字符串,则表示该位置是新添加的,WeatherCard将呈现一个组件,允许用户输入位置。如果已经输入了位置(location prop 不是空字符串),则将呈现LocationWeather组件,该组件负责检索位置的天气并将其显示在WeatherCard中。
位置天气组件
该组件接受单个 prop— location —并存储两种状态:一个用于已检索到的天气数据,另一个用于从 API 返回的任何错误消息。
要检索天气数据,可以使用带有location prop参数的效果钩子;这意味着只要location prop 发生变化,就会调用副作用,在此应用中,即组件安装时。代码如下所示:
React.useEffect(() => {
const getWeather = async () => {
const result = await getLocationWeather(location);
setWeatherData(result.success ? result.data : {});
setApiError(result.success ? "" : result.error);
};
getWeather();
}, [location]);
由于传递给useEffect的函数不能是异步函数,因此在效果函数getWeather中声明了一个异步内联函数,该函数由效果调用(但不等待)。此函数调用getLocationWeather函数,并根据结果的success属性设置天气数据和错误状态;如果 API 调用成功,则将天气数据设置为结果,将错误描述设置为空字符串;如果调用失败,则将天气数据设置为空对象,将错误描述设置为error属性。然后可以呈现组件。
目前,当应用程序等待 API 调用返回时,组件将不显示任何内容。如果调用返回很快,那么这没问题;但是,如果应用程序在慢速网络上运行或 API 运行缓慢,那么将组件视图留空并不是很好的用户体验。为了改善这种体验,如果 API 调用花费的时间超过特定长度,则显示加载指示器。为此,向组件添加一个新状态,当您想要显示加载指示器时,该状态将设置为true :
const [isLoading, setIsLoading] = React.useState(false);
然后,在useEffect中设置一个超时,该超时将在 500 毫秒后调用setIsLoading(true) ,这意味着在调用getLocationWeather 500 毫秒后,将显示加载指示器。为确保在 API 调用时间少于 500 毫秒时不显示指示器,应在调用完成后清除超时。还应在效果的返回中清除超时,以便组件不会在卸载组件后通过更新状态而造成内存泄漏。因此最终的 useEffect将如下所示:
React.useEffect(() => {
const loadingIndicatorTimeout = setTimeout(() => setIsLoading(true), 500);
const getWeather = async () => {
const result = await <span class="hljs-title fu
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~