Автор оригинала: Nishant Kumar.
React – это потрясающая библиотека для фронтенда, которую можно использовать для создания пользовательских интерфейсов.
Одна из лучших вещей в React – это то, что компоненты, которые мы создаем, инкапсулируются. Другими словами, их нельзя увидеть.
Давайте узнаем больше о том, как все это работает, создав приложение для работы с погодой с помощью React.
Как установить Node и npm
Для создания нашего приложения React нам понадобится среда выполнения под названием Node. Она в основном используется для выполнения кода JavaScript.
Чтобы загрузить ее, перейдите по адресу https://nodejs.org/en/.
Вам также понадобится npm – менеджер пакетов, созданный на основе Node. Вы можете использовать его для установки пакетов для ваших приложений JavaScript. К счастью, он поставляется вместе с Node, поэтому вам не нужно скачивать его отдельно.
Как только оба пакета будут установлены, откройте терминал или командную строку и введите node -v. Это проверит, какая версия Node у вас установлена.
Как создать приложение React
Чтобы создать наше приложение react, введите npx create-react-app <Имя вашего приложения> в терминале, или npx create-react-app my-weather-app в данном случае.
Вы увидите, что пакеты устанавливаются.
Как только пакеты будут установлены, перейдите в папку проекта и введите npm start.
Сейчас все это нам не нужно. Итак, давайте очистим некоторый код.
В вашем файле app.js уберите все внутри тега div. Удалите импорт логотипа.
После этого на выходе вы получите пустой экран.
Как установить нужные нам пакеты
Чтобы сделать это приложение более привлекательным, нам понадобятся некоторые внешние пакеты. Итак, давайте установим их.
Нам нужна библиотека Semantic React UI. Чтобы установить ее, введите в терминале следующую команду:
npm install semantic-ui-react semantic-ui-css
После его установки откройте файл index.js и импортируйте библиотеку. Просто скопируйте и вставьте следующую команду в файл index.js:
import 'semantic-ui-css/semantic.min.css'
Нам также нужен moment.js для форматирования времени. Установите его с помощью следующей команды:
npm install moment --save
Вы можете проверить файл package.json для отслеживания всех установленных пакетов.
Здесь вы можете увидеть все пакеты, которые у вас есть на данный момент.
Как создать наше погодное приложение
Для работы нашего погодного приложения нам понадобится OpenWeatherMap, сторонний API, который позволит нам получать данные о погоде.
Перейдите на сайт https://home.openweathermap.org/users/sign_up и создайте свой аккаунт.
После этого нажмите на опцию API на панели навигации. Вы увидите различные опции, такие как Текущие данные о погоде, Почасовые прогнозы на 4 часа, Прогнозы на 16 дней и другие. Это конечные точки API, которые вам понадобятся для получения данных.
Для вызова этих API вам также необходим ключ API. Чтобы получить ключ API, нажмите на свое имя пользователя в правом верхнем углу, а затем на “мои ключи API”.
Создайте API-ключ, если он еще не существует.
В основной папке приложения создайте файл с именем .env.
Это файл переменных окружения, который будет содержать все ваши конечные точки и ключи API.
REACT_APP_API_URL = 'https://api.openweathermap.org/data/2.5' REACT_APP_API_KEY = Paste your API key here. REACT_APP_ICON_URL = 'https://openweathermap.org/img/w'
Вставьте скопированный ключ API в переменную REACT_APP_API_KEY.
Как использовать React Hooks
React Hooks позволяет нам использовать и управлять состоянием в наших функциональных компонентах.
Мы будем использовать хук useState и хук useEffect. Давайте импортируем их в верхней части.
import React, { useEffect, useState } from "react";
Давайте создадим два состояния. Одно – для широты, другое – для долготы.
const [lat, setLat] = useState([]); const [long, setLong] = useState([]);
Теперь создайте функцию useEffect. Ее цель – загружать функции при загрузке и перезагрузке приложения.
useEffect(() => { navigator.geolocation.getCurrentPosition(function(position) { setLat(position.coords.latitude); setLong(position.coords.longitude); }); console.log("Latitude is:", lat) console.log("Longitude is:", long) }, [lat, long]);
Мы получаем широту и долготу с помощью navigator.geolocation и используем setLong и setLat для установки состояния долготы и широты.
import './App.css'; import React, { useEffect, useState } from "react"; export default function App() { const [lat, setLat] = useState([]); const [long, setLong] = useState([]); useEffect(() => { navigator.geolocation.getCurrentPosition(function(position) { setLat(position.coords.latitude); setLong(position.coords.longitude); }); console.log("Latitude is:", lat) console.log("Longitude is:", long) }, [lat, long]); return ( <div className="App"> </div> ); }
Вот как теперь выглядит наш файл app.js. Вы можете проверить в консоли значения широты и долготы.
Latitude is: 25.5922166 Longitude is: 85.12761069999999
Как получить наше текущее местоположение, используя широту и долготу.
Давайте создадим еще одну функцию getWeather, которая будет получать данные о погоде из Weather API, основываясь на нашей широте и долготе.
В этой функции мы используем вызов fetch для получения данных из API. Process.env.REACT_APP_API_URL получает адрес вашего API, а process.env.REACT_APP_API_KEY – ключ API из файла .env. lat и long – это широта и долгота, которые мы получили ранее.
Затем мы конвертируем данные в формат JSON.
На следующем шаге мы используем setData для сохранения нашего результата в объект данных.
await fetch(`${process.env.REACT_APP_API_URL}/weather/?lat=${lat}&lon=${long}&units=metric&APPID=${process.env.REACT_APP_API_KEY}`) .then(res => res.json()) .then(result => { setData(result) console.log(result); });
И мы регистрируем данные в консоли.
Здесь вы можете увидеть все данные о погоде, основанные на широте и долготе.
Вот наш новый файл app.js, который получает данные о погоде на основе долготы и широты:
import './App.css'; import React, { useEffect, useState } from "react"; import Weather from './components/weather'; export default function App() { const [lat, setLat] = useState([]); const [long, setLong] = useState([]); const [data, setData] = useState([]); useEffect(() => { const fetchData = async () => { navigator.geolocation.getCurrentPosition(function(position) { setLat(position.coords.latitude); setLong(position.coords.longitude); }); await fetch(`${process.env.REACT_APP_API_URL}/weather/?lat=${lat}&lon=${long}&units=metric&APPID=${process.env.REACT_APP_API_KEY}`) .then(res => res.json()) .then(result => { setData(result) console.log(result); }); } fetchData(); }, [lat,long]) return ( <div className="App"> </div> ); }
Как создать компоненты для работы с погодой
Давайте создадим наши погодные компоненты, в которых мы будем отображать данные о погоде.
В папке src создайте папку с названием components, а в ней – файл weather.js.
Теперь давайте вызовем наш компонент погоды в нашем основном файле app.js.
import './App.css'; import React, { useEffect, useState } from "react"; import Weather from './components/weather'; export default function App() { const [lat, setLat] = useState([]); const [long, setLong] = useState([]); const [data, setData] = useState([]); useEffect(() => { const fetchData = async () => { navigator.geolocation.getCurrentPosition(function(position) { setLat(position.coords.latitude); setLong(position.coords.longitude); }); await fetch(`${process.env.REACT_APP_API_URL}/weather/?lat=${lat}&lon=${long}&units=metric&APPID=${process.env.REACT_APP_API_KEY}`) .then(res => res.json()) .then(result => { setData(result) console.log(result); }); } fetchData(); }, [lat,long]) return ( <div className="App"> {(typeof data.main != 'undefined') ? ( <Weather weatherData={data}/> ): ( <div></div> )} </div> ); }
Вы можете видеть, что я включил проверку в оператор возврата. Если тип данных, которые мы получаем, не определен, он покажет нам пустой div. Поскольку функция fetch data является асинхронной функцией, включение этой проверки обязательно. Она загружает функцию после завершения выполнения всех других функций. Поэтому, если вы удалите эту проверку, вы получите ошибку.
Это происходит потому, что наше приложение отображает оператор возврата до того, как будет выполнен вызов API, и в этом случае нечего показывать, поэтому оно выбрасывает неопределенную ошибку.
Чтобы узнать больше об async/await, ознакомьтесь с этой статьей.
Как создать тело нашей программы Weather
В этой части мы будем использовать библиотеку Semantic UI для проектирования нашего интерфейса.
Давайте создадим карточку, которая будет отображать нашу информацию о погоде.
import React from 'react'; import './styles.css'; import { Card } from 'semantic-ui-react' const CardExampleCard = ({weatherData}) => ( <Card> <Card.Content> <Card.Header className="header">{weatherData.name}</Card.Header> </Card.Content> </Card> ) export default CardExampleCard;
Здесь мы импортируем карточку из semantic-ui-react, а внутри этой карточки – заголовок, который будет показывать название вашего города.
Но вопрос в том, как нам получить данные из нашего app.js в компонент weather.js?
Ответ прост. Мы можем использовать props в React для передачи данных от родительского компонента к дочернему компоненту. В нашем случае родительский компонент – это app.js, а дочерний – weather.js.
И чтобы сделать это, просто добавьте реквизит в компонент в app.js.
<Weather weatherData={data}/>
Здесь мы передаем данные с именем реквизита weatherData. И мы получим реквизит weatherData в Weather.js.
import React from 'react'; import './styles.css'; import { Card } from 'semantic-ui-react' const CardExampleCard = ({weatherData}) => ( <Card> <Card.Content> <Card.Header className="header">{weatherData.name}</Card.Header> </Card.Content> </Card> ) export default CardExampleCard;
Вы можете видеть, что мы получаем название города в соответствии с местоположением.
Аналогичным образом мы можем добавить дополнительные поля в наш компонент погоды.
import React from 'react'; import './styles.css'; import { Card } from 'semantic-ui-react' const CardExampleCard = ({weatherData}) => ( <Card> <Card.Content> <Card.Header className="header">City Name: {weatherData.name}</Card.Header> <p>Temprature: {weatherData.main.temp}</p> <p>Sunrise: {weatherData.sys.sunrise}</p> <p>Sunset: {weatherData.sys.sunset}</p> <p>Description: {weatherData.weather[0].description}</p> </Card.Content> </Card> ) export default CardExampleCard;
Мы можем получить температуру, восход, закат и описание из API.
Вы можете добавить любые другие поля, такие как влажность, скорость ветра, видимость и другие.
Как отформатировать данные и добавить сегодняшний день и дату
Давайте отформатируем данные так, чтобы их было легко понять. Мы добавим еще несколько полей.
Для начала добавьте единицу измерения температуры. Это можно сделать, добавив °C после температуры.
Также изменим восход и заход солнца на местное время.
import React from 'react'; import './styles.css'; import { Card } from 'semantic-ui-react' const CardExampleCard = ({weatherData}) => ( <Card> <Card.Content> <Card.Header className="header">City Name: {weatherData.name}</Card.Header> <p>Temprature: {weatherData.main.temp} °C</p> <p>Sunrise: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p> <p>Sunset: {new Date(weatherData.sys.sunset * 1000).toLocaleTimeString('en-IN')}</p> <p>Description: {weatherData.weather[0].main}</p> <p>Humidity: {weatherData.main.humidity} %</p> </Card.Content> </Card> ) export default CardExampleCard;
Теперь давайте добавим сегодняшний день и дату с помощью moment.js.
import moment from 'moment'; <p>Day: {moment().format('dddd')}</p> <p>Date: {moment().format('LL')}</p>
Мы импортируем пакет moment в верхней части и отображаем сегодняшний день и дату соответственно. Самое замечательное в этом пакете то, что он автоматически обновляет дату и день.
Вот как теперь выглядит наш weather.js:
import React from 'react'; import './styles.css'; import { Card } from 'semantic-ui-react'; import moment from 'moment'; const CardExampleCard = ({weatherData}) => ( <Card> <Card.Content> <Card.Header className="header">City Name: {weatherData.name}</Card.Header> <p>Temprature: {weatherData.main.temp} °C</p> <p>Sunrise: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p> <p>Sunset: {new Date(weatherData.sys.sunset * 1000).toLocaleTimeString('en-IN')}</p> <p>Description: {weatherData.weather[0].main}</p> <p>Humidity: {weatherData.main.humidity} %</p> <p>Day: {moment().format('dddd')}</p> <p>Date: {moment().format('LL')}</p> </Card.Content> </Card> ) export default CardExampleCard;
Выше показан наш результат.
Давайте сделаем некоторые изменения в стиле
Теперь, когда у нас есть все наши данные, давайте придадим им стиль, чтобы сделать их более привлекательными.
Прежде всего, давайте сделаем карточку больше, изменим границу-радиус, добавим более холодный шрифт и цвет, а также уберем выравнивание текста.
import React from 'react'; import './styles.css'; import moment from 'moment'; const CardExampleCard = ({weatherData}) => ( <div className="main"> <p className="header">{weatherData.name}</p> <div> <p className="day">Day: {moment().format('dddd')}</p> </div> <div> <p className="temp">Temprature: {weatherData.main.temp} °C</p> </div> </div> ) export default CardExampleCard;
@import url('https://fonts.googleapis.com/css2?family=Recursive&display=swap'); .main{ width: 700px; border-radius: 15px; background-color: #01579b; } .header{ background-color: #424242; color: whitesmoke; padding: 10px; font-size: 28px; border-radius: 15px; font-family: 'Recursive', sans-serif; } .day{ padding: 15px; color: whitesmoke; font-family: 'Recursive', sans-serif; font-size: 24px; font-weight: 600; } .temp{ padding: 15px; color: whitesmoke; font-family: 'Recursive', sans-serif; font-size: 18px; }
Давайте воспользуемся flexbox, чтобы расположить данные по столбцам.
<div className="flex"> <p className="day">Day: {moment().format('dddd')}</p> </div> <div className="flex"> <p className="temp">Temprature: {weatherData.main.temp} °C</p> </div>
Назовите divs как ‘flex‘ и добавьте следующее свойство в styles.css.
.flex{ display: flex; justify-content: space-between; }
Теперь наш weather.js будет выглядеть примерно так.
import React from 'react'; import './styles.css'; import moment from 'moment'; const CardExampleCard = ({weatherData}) => ( <div className="main"> <p className="header">{weatherData.name}</p> <div className="flex"> <p className="day">Day: {moment().format('dddd')}</p> <p className="day">{moment().format('LL')}</p> </div> <div className="flex"> <p className="temp">Temprature: {weatherData.main.temp} °C</p> <p className="temp">Humidity: {weatherData.main.humidity} %</p> </div> </div> ) export default CardExampleCard;
Аналогично добавьте оставшиеся поля.
import React from 'react'; import './styles.css'; import moment from 'moment'; const WeatherCard = ({weatherData}) => ( <div className="main"> <p className="header">{weatherData.name}</p> <div className="flex"> <p className="day">{moment().format('dddd')}, <span>{moment().format('LL')}</span></p> <p className="description">{weatherData.weather[0].main}</p> </div> <div className="flex"> <p className="temp">Temprature: {weatherData.main.temp} °C</p> <p className="temp">Humidity: {weatherData.main.humidity} %</p> </div> <div className="flex"> <p className="sunrise-sunset">Sunrise: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p> <p className="sunrise-sunset">Sunset: {new Date(weatherData.sys.sunset * 1000).toLocaleTimeString('en-IN')}</p> </div> </div> ) export default WeatherCard;
@import url('https://fonts.googleapis.com/css2?family=Recursive&display=swap'); .main{ width: 700px; border-radius: 20px; background-color: #01579b; } .top{ height: 60px; background-color: #424242; color: whitesmoke; padding: 10px; border-radius: 20px 20px 0 0; font-family: 'Recursive', sans-serif; display: flex; justify-content: space-between; } .header{ background-color: #424242; color: whitesmoke; margin: 10px 0px 0px 10px; font-size: 25px; border-radius: 20px 20px 0 0; font-family: 'Recursive', sans-serif; } .day{ padding: 15px; color: whitesmoke; font-family: 'Recursive', sans-serif; font-size: 24px; font-weight: 600; } .temp{ padding: 15px; color: whitesmoke; font-family: 'Recursive', sans-serif; font-size: 18px; } .flex{ display: flex; justify-content: space-between; } .sunrise-sunset{ padding: 15px; color: whitesmoke; font-family: 'Recursive', sans-serif; font-size: 16px; } .description{ padding: 15px; color: whitesmoke; font-family: 'Recursive', sans-serif; font-size: 24px; font-weight: 600; }
Вот как теперь выглядит наше приложение:
Как добавить кнопку обновления.
Давайте добавим кнопку обновления в верхней части нашей страницы.
import React from 'react'; import './styles.css'; import moment from 'moment'; import { Button } from 'semantic-ui-react'; const refresh = () => { window.location.reload(); } const WeatherCard = ({weatherData}) => ( <div className="main"> <div className="top"> <p className="header">{weatherData.name}</p> <Button className="button" inverted color='blue' circular icon='refresh' onClick={refresh} /> </div> <div className="flex"> <p className="day">{moment().format('dddd')}, <span>{moment().format('LL')}</span></p> <p className="description">{weatherData.weather[0].main}</p> </div> <div className="flex"> <p className="temp">Temprature: {weatherData.main.temp} °C</p> <p className="temp">Humidity: {weatherData.main.humidity} %</p> </div> <div className="flex"> <p className="sunrise-sunset">Sunrise: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p> <p className="sunrise-sunset">Sunset: {new Date(weatherData.sys.sunset * 1000).toLocaleTimeString('en-IN')}</p> </div> </div> ) export default WeatherCard;
.button{ width: 35px; height: 35px; }
Вы видите кнопку, которая запускает функцию обновления. Когда вы нажмете на нее, страница обновится.
Как добавить лоадер при загрузке нашего приложения
Давайте добавим лоадер, чтобы сделать приложение еще более удивительным.
Импортируйте Loader из Semantic UI и добавьте его в функцию возврата, где мы проверяем наличие неопределенных данных.
import { Dimmer, Loader } from 'semantic-ui-react'; <div className="App"> {(typeof data.main != 'undefined') ? ( <Weather weatherData={data}/> ): ( <div> <Dimmer active> <Loader>Loading..</Loader> </Dimmer> </div> )} </div>
Давайте вспомним, что мы сделали
Мы создали React-приложение, которое показывает текущую погоду в зависимости от вашего местоположения.
Давайте пройдемся по всему, что мы сделали до сих пор.
Мы узнали о State и Props
State и Props – это очень мощные функции в React. Они используются для управления данными и контроля их потока в различных компонентах.
В нашем приложении мы управляем состоянием, которое является состоянием приложения. Например, название города, температура, дата, влажность и все остальное. Они меняются от пользователя к пользователю, в зависимости от его местоположения.
Реквизиты, с другой стороны, используются для передачи данных между компонентами. Мы получаем данные в нашем файле app.js, а считываем их в файле weather.js. Помните, что реквизиты можно использовать только для передачи данных от родительского компонента к дочернему.
Мы использовали React Hooks
Если вы использовали компоненты классов, то наверняка знаете о методах жизненного цикла. Если нет, то это методы, которые вызываются при рендеринге или повторном рендеринге нашей страницы. Но мы не можем использовать методы жизненного цикла в функциональных компонентах, так как они специально созданы для компонентов класса.
Поэтому альтернативой являются React Hooks. В нашем приложении мы использовали два хука. Один из них – useState – используется для управления состоянием приложения. Другой – useEffect, который загружается при рендеринге или загрузке страницы.
Мы опробовали Semantic UI
Semantic UI – это библиотека для React, в которой есть предопределенные потрясающие компоненты.
Это все, друзья. Вы можете добавить в приложение дополнительные функции, например, прогноз на пять дней, иконки и многое другое.
Вы можете найти код на Github, если хотите экспериментировать дальше.
Оригинал: “https://www.freecodecamp.org/news/learn-react-by-building-a-weather-app/”