使用 React Hooks 创建天气应用:第一部分
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
在本教程中,我们将创建一个 React 天气应用,并使用 React 最新的 Hooks。完成本教程后,您将掌握 React 最常用的 Hooks 以及如何创建自定义 Hook。
您可以查看本教程中我们将创建的应用示例。
先决条件
- 熟悉 HTML
- Javascript、ES6,了解 React 和 Javascript 的区别
- 具备 React 基础知识,例如 props、组件、单向数据流
我们将涵盖的内容
- 使用 state 和 useState
- 使用 useEffect 获取 API
- 在我们的应用程序中使用自定义钩子
完成本教程后,您将掌握以下技能:
使用 React Hooks 创建天气应用程序的实践操作和真实场景
什么是 React Hooks?
Hooks是React 16.8新增的功能。借助Hooks,我们可以在不编写类的情况下使用state和其他React特性。
在 Hooks 出现之前,我们需要理解JavaScript 中`this`关键字的工作原理,并且要记住在类组件中绑定事件处理程序。
当时并没有专门的方法来重用有状态组件的逻辑,这使得代码难以理解。
我们需要以更好的方式共享状态逻辑。React 的设计初衷是渲染组件,它并不了解路由、数据获取或项目架构。因此,React Hooks 应运而生。
Hooks 只是从 React 官方页面导出的一些函数。它们允许我们以不同的方式操作组件。
Hooks 允许将可重用的逻辑附加到现有组件,并在 React 函数式组件内部使用状态和生命周期方法。
我们可以将组件内部的逻辑组织成可重用的独立单元。Hooks 使开发者能够将展示逻辑(与组件在页面上的显示方式相关的逻辑)与业务逻辑(与处理、操作和存储业务对象相关的逻辑)分离。
使用钩子函数有一些规则。以下是这些规则:
- 仅在组件的顶层调用钩子。
- 不要在循环、条件语句或嵌套函数中调用钩子函数。
- 仅从 React 函数调用 hooks
- 应该在 React 函数组件内部调用它们,而不仅仅是在任何普通的 JavaScript 函数中调用它们。
好了,现在我们开始使用我们的应用程序。
应用工具
- [x] 安装NodeJS并确保是 LTS(长期支持)版本。LTS 版本是 NodeJS 的一个稳定性较低的版本。我们将使用 NPM(Node 包管理器)来安装create-react-app。

- [x] 安装你喜欢的代码编辑器或集成开发环境 (IDE)。我将使用 Visual Studio Code。你可以从这个网站下载它。它是免费的。

- [x] create-react-app是一个 npm 包,我们可以使用它来引导我们的 React 应用程序而无需任何配置。

让我们来安装项目。打开终端,并cd进入你想创建项目的目录。
cd desktop
# type this command to install create-react-app, you can give any name for the app.
npx create-react-app weather-app
现在,我们等待项目创建完成,现在所有软件包都已安装完毕,我们可以使用它了。
让我们进入项目文件夹,输入项目名称,然后cd进入其中。
cd weather-app
# open the project files with Visual Studio or any code editor
#start the app
npm start
现在我们可以看到应用程序已经启动并运行。在启动应用程序之前,让我们进行一些清理工作,删除一些我们不会使用的文件。
让我们把它App.test.js, index.css, logo.svg, setupTests.js从src文件夹中移除。您可以从下面的代码片段中复制并粘贴 App.js 和 index.js 的基本结构。
// App.js
import React from 'react';
import './App.css';
function App() {
return <div className="App"></div>;
}
export default App;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
此外,我们还可以logo从文件夹中删除文件public,现在我的文件看起来是这样的:
探索 Open Weather 应用和样式
获取我们的 API 密钥
让我们打开天气地图,获取我们的 API 密钥来获取真实的天气数据。
从页面选择“5 天/3 小时天气预报”。通过此 API,我们可以获取指定城市未来 5 天的天气数据。
但在使用开放天气地图之前,我们需要一个API 密钥。为此,请创建一个帐户,然后转到“API 密钥”选项卡查看您的 API 密钥。
让我们查看页面上的示例,打开一个新标签页并粘贴此网址。
# replace API key with your API key
api.openweathermap.org/data/2.5/forecast?q=London,us&appid={API key}
现在,我们可以看到JSON数据了。
默认数据采用英制单位,我们可以通过指定另一个查询参数将其更改为公制单位。如果您习惯使用英制单位,则无需更改查询。
api.openweathermap.org/data/2.5/forecast?q=London,us&appid={API key}&units=metric
现在,让我们看看数据能提供什么信息。我们将使用天气图标,来看看它的代码代表什么。在文档中,我们可以找到这个页面,了解图标代码的含义。我们将使用这个URLhttp://openweathermap.org/img/wn/10d@2x.png作为图片源。
我们将获取未来五天的最低和最高温度,并配以图标。
现在,让我们在目录apis下创建一个名为 `directory` 的新文件夹src,并在其中创建一个名为config.js`API key` 的新文件,并将此文件添加到您的配置.gitignore文件中,以防止 API 密钥泄露。此外,让我们把 `<your_file_name>` 文件放在baseUrl这里。稍后我们将回到这里添加获取数据的逻辑。
// apis/config.js
export const API_KEY = [YOUR_API_KEY];
export const API_BASE_URL = 'http://api.openweathermap.org/';
应用样式
我们将使用 React Bootstrap 来设计应用程序的样式。您可以查看相关文档。
让我们把 react-bootstrap 安装到我们的项目中。
npm install react-bootstrap bootstrap
现在,我们需要将 CSS 添加到我们的项目中src > index.js。
// index.js
import 'bootstrap/dist/css/bootstrap.min.css';
创建我们的第一个组件🥳
让我们先创建第一个组件,并向用户展示我们的 API 数据。
在文件夹内src,我们再创建一个名为 `<folder_name>` 的文件夹components。现在,创建我们的第一个组件,并将其命名为 ` WeatherCard.js
<component_name>`。这个组件将是一个函数式组件,它会接收一些 props 并显示它们。我们将使用 `<style>` 标签Bootstrap Card component来添加一些样式。现在,我们可以将 Bootstrap 中的`Card` 组件
复制到我们的组件中。我们不需要 `<style>`和 `<style> ` 标签,我们将删除它们。Card.TextButton
// components/WeatherCard.js
import React from 'react';
import {Card} from 'react-bootstrap';
const WeatherCard = (props) => {
return (
<Card style={{width: '18rem'}}>
<Card.Img variant="top" src="holder.js/100px180" />
<Card.Body>
<Card.Title>Card Title</Card.Title>
</Card.Body>
</Card>
);
};
export default WeatherCard;
我们想显示日期minimum的早晚maximum temperatures时间,但dt日期时间是 Unix 时间戳格式。此外,我们还要显示main天气。
现在,让我们提取属性并将其显示在 JSX 中。属性的名称与我们从 API 获取的 JSON 数据的名称相同。
通过图标,我们可以获取天气状况列表。每个图标都有一个不同的代码编号。
- 示例网址:
http://openweathermap.org/img/wn/10d@2x.png
我们将10d用该icon属性替换它,使其成为动态的。
// components/WeatherCard.js
import React from 'react';
import {Card} from 'react-bootstrap';
const WeatherCard = ({dt, temp_min, temp_max, main, icon}) => {
// create a date object with Date class constructor
const date = new Date(dt);
return (
<Card style={{width: '18rem'}}>
<Card.Img
variant="top"
// get the src from example url and pass the icon prop for icon code
src={`http://openweathermap.org/img/wn/${icon}@2x.png`}
/>
<Card.Body>
<Card.Title>{main}</Card.Title>
{/* datetime is received in milliseconds, let's turn into local date time */}
<p>
{date.toLocaleDateString()} - {date.toLocaleTimeString()}
</p>
{/* minimum temperature */}
<p>Min: {temp_min}</p>
{/* maximum temperature */}
<p>Max: {temp_max}</p>
</Card.Body>
</Card>
);
};
export default WeatherCard;
现在,让我们把WeatherCard组件导入进去App.js。然后传递我们的 props,目前我们将传递硬编码的值。
// App.js
import React from 'react';
import WeatherCard from './components/WeatherCard';
import './App.css';
const App = () => {
return (
<div className="App">
{/* dt is in unix-seconds but javascript uses milliseconds, multiply with 1000 */}
<WeatherCard
dt={1602104400 * 1000}
temp_min="22.67"
temp_max="24.39"
main="Clear"
icon="01d"
/>
</div>
);
};
export default App;
现在,让我们从终端启动应用程序npm start。可以看到天气数据已经显示出来。我们将使用此组件显示未来 5 天的天气情况。
城市选择器组件
我们将创建一个新组件,用户可以选择一个城市,然后我们将显示该城市的天气数据。
在我们的组件中,我们将创建input一个按钮和一个事件处理程序button。当用户点击按钮时,我们将获取该城市的天气预报。
我们将使用 Bootstrap Layout 来创建行和列。您可以通过此链接找到相关文档。
现在,让我们进入 components 文件夹,创建另一个名为 的文件夹CitySelector.js,并创建我们的样板代码。
使用状态钩子
状态有助于构建高性能的 Web 应用程序。为了跟踪应用程序逻辑,我们需要使用状态。我们可以通过状态的改变来反映任何 UI 或用户界面的变化。
为了能够改变按钮的状态,我们需要一个名为 `setState` 的特殊钩子useState。通过`setState` useState,我们可以为函数式组件添加状态。
useState返回一个包含两个元素的数组:第一个元素是状态的当前值,第二个元素是状态的设置函数。状态用于跟踪状态的值。每当状态更新时,它都应该重新渲染 JSX 元素。设置函数将用于更新状态值。
在类组件中,状态始终是一个对象;而使用 useState hook 后,状态就不一定是对象了。
处理对象或数组时,务必展开状态变量,然后再调用 setter 函数。
每次重新渲染时,我们都不会改变我们的状态,而是会得到一个全新的状态;我们可以使用 setter 函数来改变我们的状态。
我们需要包含一个状态属性,即城市。为了在组件中使用useState,我们必须先导入useState。useState是一个命名导出,因此我们将使用花括号将其导出。
import React, { useState } from 'react';
我们的目标是在用户点击按钮时更新状态。
我们需要定义一个新变量并将其设置为useState钩子函数。在钩子函数内部,我们需要将该变量的initial值作为参数传递,参数值为空字符串。
// components/CitySelector
import React, {useState} from 'react';
const CitySelector = () => {
const [city, setCity] = useState('');
return <div></div>;
};
export default CitySelector;
我们将从 Bootstrap 中添加Row、Col、FormControl 和 Button组件来创建 JSX 元素。FormControl 组件用于创建我们的input元素,我们需要通过传递参数来获取它的值。目前,event.target.value
我们将为该Button组件传递一个函数,稍后会用它来显示数据。
// components/CitySelector.js
import React, {useState} from 'react';
import {Row, Col, FormControl, Button} from 'react-bootstrap';
const CitySelector = () => {
const [city, setCity] = useState('');
return (
<>
<Row>
<Col>
<h1>Search your city</h1>
</Col>
</Row>
<Row>
{/* xs={4} takes the one third of the page*/}
<Col xs={4} className="text-center">
<FormControl
placeholder="Enter city"
// update city value with the user's input
onChange={(event) => setCity(event.target.value)}
// value will be the currently selected city
value={city}
/>
</Col>
</Row>
<Row>
<Col>
{/* event handler for button click */}
<Button onClick={onSearch} }>Check Weather</Button>
</Col>
</Row>
</>
);
};
export default CitySelector;
现在,让我们把 CitySelector 组件导入到 App.js 中。同时,我们可以移除硬编码的 WeatherCard 组件,现在可以直接从用户输入获取城市数据。
我们的应用程序组件现在看起来像这样。另外,我还添加了一个来自 Bootstrap 的容器。
// App.js
import React from 'react';
import CitySelector from './components/CitySelector';
import './App.css';
import {Container} from 'react-bootstrap';
const App = () => {
return (
<Container className="App">
<CitySelector />
</Container>
);
};
export default App;
另外,请将此 CSS 代码复制粘贴到您的App.css文件中。
/* App.css */
.App {
text-align: center;
}
.row {
justify-content: center;
margin: 15px 0;
}
显示 API 结果
现在,是时候在我们的应用程序中显示 API 数据了。
让我们回到CitySelector组件并调用我们的 API。
首先,让我们为我们的onSearch函数创建一个匿名函数。
要从外部资源获取数据或检索数据,我们将使用fetch浏览器 API。Fetch 函数会接收我们的url调用。我们需要从文件中获取 `<your_name> baseUrl` 和 `<your_name>` 。让我们将它们导入到文件中。Api keyconfig.js
import {API_KEY, API_BASE_URL} from '../apis/config';
Fetch 返回一个 Promise,我们需要等待它,.then之后我们的响应将以json格式表示,我们需要提取响应体,最后,我们将得到我们的result。
现在onSearch函数应该如下所示:
// components/CitySelector.js
const onSearch = () => {
fetch(`${ API_BASE_URL}/data/2.5/forecast?q=${city}&appid=${API_KEY}&units=metric`)
.then((response) => response.json())
.then((result) => console.log(result));
};
此外,我们还可以在用户按下Enter按键时显示数据。让我们用 JavaScript 来实现这个功能。
添加onKeyDown到`<head>` 标签中FormControl (input),它将接收一个包含该事件的回调函数。
// components/CitySelector.js
const onKeyDown = (event) => {
if (event.keyCode === 13) {
onSearch();
}
};
<Row>
<Col xs={4} className="text-center">
<FormControl
placeholder="Enter city"
onChange={(event) => setCity(event.target.value)}
value={city}
// add onKeyDown
onKeyDown={onKeyDown}
/>
</Col>
</Row>;
为了显示我们的数据,我们需要为我们创建另一个状态results。
// components/CitySelector.js
const CitySelector = () => {
const [city, setCity] = useState('');
const [results, setResults] = useState(null);
const onSearch = () => {
fetch(
`${API_BASE_URL}/data/2.5/forecast?q=${city}&appid=${API_KEY}&units=metric`
)
.then((response) => response.json())
// update the results
.then((results) => setResults(results));
};
return (
<>
<Row>
<Col>
<h1>Search your city</h1>
</Col>
</Row>
<Row>
<Col xs={4} className="text-center">
<FormControl
placeholder="Enter city"
onChange={(event) => setCity(event.target.value)}
value={city}
/>
</Col>
</Row>
<Row>
<Col>
<Button onClick={onSearch}>Check Weather</Button>
</Col>
</Row>
</>
);
};
好了,这篇文章就到这里。接下来useEffect Hook and custom hooks,我们将继续教程的第二部分。
感谢您的阅读。喜欢这篇文章吗?不妨请我喝杯咖啡,支持我继续创作。
文章来源:https://dev.to/hulyamasharipov/create-a-weather-app-with-react-hooks-part-1-4055









