发布于 2026-01-06 8 阅读
0

Create a Weather App with React Hooks: Part 1 DEV's Worldwide Show and Tell Challenge Presented by Mux: Pitch Your Projects!

使用 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-appnode.png
  • [x] 安装你喜欢的代码编辑器或集成开发环境 (IDE)。我将使用 Visual Studio Code。你可以从这个网站下载它。它是免费的。vscode.png
  • [x] create-react-app是一个 npm 包,我们可以使用它来引导我们的 React 应用程序而无需任何配置。cra.png

让我们来安装项目。打开终端,并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
Enter fullscreen mode Exit fullscreen mode

现在,我们等待项目创建完成,现在所有软件包都已安装完毕,我们可以使用它了。

终端

让我们进入项目文件夹,输入项目名称,然后cd进入其中。

cd weather-app
# open the project files with Visual Studio or any code editor

#start the app
npm start
Enter fullscreen mode Exit fullscreen mode

app.png

现在我们可以看到应用程序已经启动并运行。在启动应用程序之前,让我们进行一些清理工作,删除一些我们不会使用的文件。

让我们把它App.test.js, index.css, logo.svg, setupTests.jssrc文件夹中移除。您可以从下面的代码片段中复制并粘贴 App.js 和 index.js 的基本结构。

// App.js

import React from 'react';
import './App.css';

function App() {
  return <div className="App"></div>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode
// 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();
Enter fullscreen mode Exit fullscreen mode

此外,我们还可以logo从文件夹中删除文件public,现在我的文件看起来是这样的:

fs.png

探索 Open Weather 应用和样式

获取我们的 API 密钥

让我们打开天气地图,获取我们的 API 密钥来获取真实的天气数据。

从页面选择“5 天/3 小时天气预报”。通过此 API,我们可以获取指定城市未来 5 天的天气数据。
但在使用开放天气地图之前,我们需要一个API 密钥。为此,请创建一个帐户,然后转到“API 密钥”选项卡查看您的 API 密钥。

openWeather.png

key.png

让我们查看页面上的示例,打开一个新标签页并粘贴此网址。

# replace API key with your API key
api.openweathermap.org/data/2.5/forecast?q=London,us&appid={API key}
Enter fullscreen mode Exit fullscreen mode

现在,我们可以看到JSON数据了。

api.png

默认数据采用英制单位,我们可以通过指定另一个查询参数将其更改为公制单位。如果您习惯使用英制单位,则无需更改查询。

api.openweathermap.org/data/2.5/forecast?q=London,us&appid={API key}&units=metric
Enter fullscreen mode Exit fullscreen mode

api_call.png

现在,让我们看看数据能提供什么信息。我们将使用天气图标,来看看它的代码代表什么。在文档中,我们可以找到这个页面,了解图标代码的含义。我们将使用这个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/';
Enter fullscreen mode Exit fullscreen mode

应用样式

我们将使用 React Bootstrap 来设计应用程序的样式。您可以查看相关文档

让我们把 react-bootstrap 安装到我们的项目中。

npm install react-bootstrap bootstrap
Enter fullscreen mode Exit fullscreen mode

现在,我们需要将 CSS 添加到我们的项目中src > index.js

// index.js
import 'bootstrap/dist/css/bootstrap.min.css';
Enter fullscreen mode Exit fullscreen mode

创建我们的第一个组件🥳

让我们先创建第一个组件,并向用户展示我们的 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;
Enter fullscreen mode Exit fullscreen mode

我们想显示日期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;
Enter fullscreen mode Exit fullscreen mode

现在,让我们把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;
Enter fullscreen mode Exit fullscreen mode

现在,让我们从终端启动应用程序npm start。可以看到天气数据已经显示出来。我们将使用此组件显示未来 5 天的天气情况。

weather-1.png

城市选择器组件

我们将创建一个新组件,用户可以选择一个城市,然后我们将显示该城市的天气数据。

在我们的组件中,我们将创建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;
Enter fullscreen mode Exit fullscreen mode

我们将从 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;
Enter fullscreen mode Exit fullscreen mode

现在,让我们把 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;
Enter fullscreen mode Exit fullscreen mode

另外,请将此 CSS 代码复制粘贴到您的App.css文件中。

/* App.css */

.App {
  text-align: center;
}

.row {
  justify-content: center;
  margin: 15px 0;
}
Enter fullscreen mode Exit fullscreen mode

显示 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 fullscreen mode Exit fullscreen mode

api.gif

此外,我们还可以在用户按下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>;
Enter fullscreen mode Exit fullscreen mode

为了显示我们的数据,我们需要为我们创建另一个状态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>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

好了,这篇文章就到这里。接下来useEffect Hook and custom hooks,我们将继续教程的第二部分。

感谢您的阅读。喜欢这篇文章吗?不妨请我喝杯咖啡,支持我继续创作。

文章来源:https://dev.to/hulyamasharipov/create-a-weather-app-with-react-hooks-part-1-4055