Artigo original: How to Build a Weather Application with React and React Hooks
O React é uma biblioteca de front-end incrível que você pode usar para criar interfaces de usuário.
Uma das melhores coisas sobre o React é que os componentes que criamos são encapsulados. Em outras palavras, eles não podem ser vistos.
Vamos aprender mais sobre como tudo isso funciona criando uma aplicação de previsão do tempo usando o React.
Como instalar o Node e o npm
Para criar nossa aplicação em React, precisamos de um ambiente de tempo de execução chamado Node. Ele é principalmente usado para executar código JavaScript.
Para baixá-lo, vá para https://nodejs.org/en/.
Você também precisará do npm, que é um gerenciador de pacotes utilizado no Node. Você pode usá-lo para instalar pacotes para suas aplicações em JavaScript. Felizmente, ele vem com o Node. Então, você não precisa baixá-lo separadamente.
Uma vez que os dois estejam instalados, abra seu terminal ou prompt de comando e digite node -v
. Isso verifica qual versão do Node você tem.
Como criar uma aplicação em React
Para criar nossa aplicação em React, digite npx create-react-app <nome do seu app>
no seu terminal, ou npx create-react-app my-weather-app
, neste caso.
Você verá que os pacotes estão sendo instalados.
Uma vez que os pacotes estiverem prontos, entre na pasta do projeto e digite npm start
.
Você verá o template padrão do create-react-app
para o React, assim:


Não precisamos de tudo isso agora. Então, vamos limpar um pouco do código.
No seu arquivo app.js, limpe tudo dentro da tag div
. Remova a importação do logo.
Você receberá uma tela em branco na saída depois de fazer isso.

Como instalar os pacotes necessários
Para tornar essa aplicação mais atraente, precisamos de alguns pacotes externos. Então, vamos instalá-los.
Precisamos da biblioteca Semantic UI React. Para instalá-la, digite o seguinte comando no terminal:
npm install semantic-ui-react semantic-ui-css
Uma vez instalada, abra index.js e importe a biblioteca. Apenas copie e cole o seguinte comando no seu arquivo index.js:
import 'semantic-ui-css/semantic.min.css'
Também precisamos do moment.js para formatar o horário e data. Instale-o usando o seguinte comando:
npm install moment --save
Você pode verificar seu arquivo package.json
para acompanhar todos os pacotes instalados.

Aqui, você pode ver todos os pacotes que possui até agora.
Como criar nossa aplicação de previsão do tempo
Para fazer nossa aplicação de previsão do tempo funcionar, precisamos do OpenWeatherMap, uma API de terceiros que nos permitirá buscar os dados do clima.
Acesse https://home.openweathermap.org/users/sign_up e crie sua própria conta.
Após finalizar, clique na opção API, na barra de navegação. Você verá diferentes opções como Current Weather Data (Dados meteorológicos atuais), Hourly Forecast 4 days (Previsões de hora em hora para 4 dias), Daily Forecast 16 days (Previsões diárias para 16 dias), entre outras. Esses são endpoints da API dos quais você precisará para buscar os dados.

Você também precisa de uma chave de API para chamar essas APIs. Para obter sua chave de API, clique em seu nome de usuário no canto superior direito, e depois em "My API keys" (minhas chaves de API).
Crie uma chave de API, se ela ainda não existir.
Na sua pasta principal da aplicação, crie um arquivo chamado .env.
Esse é um arquivo de variáveis de ambiente, que conterá todos os seus endpoints e chaves de API.
REACT_APP_API_URL = 'https://api.openweathermap.org/data/2.5'
REACT_APP_API_KEY = (Cole sua chave de API aqui)
REACT_APP_ICON_URL = 'https://openweathermap.org/img/w'
Cole sua chave API copiada na variável REACT_APP_API_KEY.
Como usar os React Hooks
Os React Hooks nos permitem usar e gerenciar o state (em português, estado) em nossos componentes funcionais.
Vamos usar os hooks useState
e useEffect
. Vamos importá-los no topo.
import React, { useEffect, useState } from "react";
Vamos criar dois states, um para latitude e outro para longitude.
const [lat, setLat] = useState([]);
const [long, setLong] = useState([]);
Agora, crie a função useEffect
. Seu objetivo é carregar as funções quando a aplicação for carregada e recarregada.
useEffect(() => {
navigator.geolocation.getCurrentPosition(function(position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
console.log("A latitude é:", lat)
console.log("A longitude é:", long)
}, [lat, long]);
Obtivemos nossa latitude e longitude usando navigator.geolocation
e usamos setLong e setLat para definir os states de longitude e de latitude.
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>
);
}
Nosso arquivo app.js está assim agora. Você pode verificar o console para os valores de latitude e longitude.
A latitude é: 25.5922166
A longitude é: 85.12761069999999
Como obter nossa localização atual usando a latitude e a longitude
Vamos criar outra função getWeather, que buscará os dados meteorológicos da Weather API, com base em nossa latitude e longitude.
Nessa função, estamos usando uma chamada de fetch para obter os dados da API. process.env.REACT_APP_API_URL obtém o endereço da sua API e process.env.REACT_APP_API_KEY obtém sua chave de API do arquivo .env. lat
e long
são a latitude e longitude que obtivemos anteriormente.
Então, convertemos os dados para o formato JSON.
No próximo passo, usamos setData para armazenar nosso resultado no objeto data.
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);
});
Depois, registramos os dados no console.

Aqui, você pode ver todos os dados meteorológicos com base em nossa latitude e longitude.
Este é o nosso novo arquivo app.js, que busca os dados meteorológicos com base na longitude e na latitude:
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>
);
}
Como criar os componentes meteorológicos
Vamos criar nossos componentes meteorológicos (Weather), onde exibiremos nossos dados meteorológicos.
Na sua pasta src, crie uma pasta chamada components. Nela, crie um arquivo chamado weather.js.
Agora, vamos chamar nosso componente meteorológico em nosso arquivo principal 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>
);
}
Você pode ver que incluí uma verificação na instrução de retorno. Se o tipo de dados que estamos obtendo for undefined
, ele nos mostrará uma div vazia. Como a busca de dados é uma função async, é obrigatório incluir essa verificação. Ela carrega a função após todas as outras funções terem sido executadas. Então, se você remover essa verificação, receberá um erro.

Isso ocorre porque nossa aplicação renderiza a instrução de retorno antes que a chamada à API seja feita. Não há nada para mostrar nesse caso. Então, ela lança um erro indefinido.
Para saber mais sobre async/await, confira este artigo (texto em inglês).
Como criar o corpo do nosso componente Weather
Para esta parte, vamos usar a biblioteca Semantic UI para projetar nossa interface.
Vamos criar um card, que exibirá nossas informações meteorológicas.
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;
Aqui, importamos um card do semantic-ui-react e, dentro dele, um cabeçalho que mostrará o nome da sua cidade.
A questão é: como obtemos dados do nosso app.js para o componente weather.js?
A resposta é simples. Podemos usar props no React para enviar dados de um componente pai para um componente filho. No nosso caso, nosso componente pai é o app.js e nosso componente filho é o weather.js.
Para fazer isso, basta adicionar as props no componente em app.js.
<Weather weatherData={data}/>
Aqui, estamos passando os dados com o nome da prop como weatherData. Receberemos as props weatherData em 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;

Você pode ver que obtivemos o nome da cidade de acordo com a localização.
Da mesma maneira, podemos adicionar mais campos ao nosso componente de previsão do tempo.
import React from 'react';
import './styles.css';
import { Card } from 'semantic-ui-react'
const CardExampleCard = ({weatherData}) => (
<Card>
<Card.Content>
<Card.Header className="header">Nome da Cidade: {weatherData.name}</Card.Header>
<p>Temperatura: {weatherData.main.temp}</p>
<p>Nascer do Sol: {weatherData.sys.sunrise}</p>
<p>Pôr do Sol: {weatherData.sys.sunset}</p>
<p>Descrição: {weatherData.weather[0].description}</p>
</Card.Content>
</Card>
)
export default CardExampleCard;
Podemos obter a temperatura, o horário do nascer do sol, do pôr do sol e a descrição do clima atual da API.

Você pode adicionar quaisquer outros campos que desejar, como umidade, velocidade do vento, visibilidade e mais.
Como formatar os dados e adicionar o dia e a data de hoje
Vamos formatar os dados para que sejam fáceis de entender. Vamos adicionar mais alguns campos.
Para começar, adicione a unidade de temperatura. Você pode fazer isso adicionando °C após a temperatura.
Além disso, vamos mudar o horário do nascer e pôr do sol para a hora local.
import React from 'react';
import './styles.css';
import { Card } from 'semantic-ui-react'
const CardExampleCard = ({weatherData}) => (
<Card>
<Card.Content>
<Card.Header className="header">Nome da Cidade: {weatherData.name}</Card.Header>
<p>Temperatura: {weatherData.main.temp} °C</p>
<p>Nascer do Sol: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p>
<p>Pôr do Sol: {new Date(weatherData.sys.sunset * 1000).toLocaleTimeString('en-IN')}</p>
<p>Descrição: {weatherData.weather[0].main}</p>
<p>Umidade: {weatherData.main.humidity} %</p>
</Card.Content>
</Card>
)
export default CardExampleCard;
Agora, vamos adicionar o dia e a data de hoje usando o moment.js.
import moment from 'moment';
<p>Dia: {moment().format('dddd')}</p>
<p>Data: {moment().format('LL')}</p>
Importamos o pacote do moment no topo e exibimos o dia e a data de hoje, respectivamente. A grande coisa sobre este pacote é que ele atualiza automaticamente a data e o dia.
É assim que nosso weather.js se parece agora:
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">Nome da Cidade: {weatherData.name}</Card.Header>
<p>Temperatura: {weatherData.main.temp} °C</p>
<p>Nascer do Sol: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p>
<p>Pôr do Sol: {new Date(weatherData.sys.sunset * 1000).toLocaleTimeString('en-IN')}</p>
<p>Descrição: {weatherData.weather[0].main}</p>
<p>Umidade: {weatherData.main.humidity} %</p>
<p>Dia: {moment().format('dddd')}</p>
<p>Data: {moment().format('LL')}</p>
</Card.Content>
</Card>
)
export default CardExampleCard;

Vamos estilizar
Agora que temos todos os nossos dados, vamos estilizá-los para torná-los mais atraentes.
Primeiramente, vamos tornar o card maior, mudar o border-radius
, adicionar uma fonte mais legal, uma cor e remover o alinhamento de texto.
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">Dia: {moment().format('dddd')}</p>
</div>
<div>
<p className="temp">Temperatura: {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;
}

Vamos usar o flexbox para organizar os dados em colunas.
<div className="flex">
<p className="day">Dia: {moment().format('dddd')}</p>
</div>
<div className="flex">
<p className="temp">Temperatura: {weatherData.main.temp} °C</p>
</div>
Nomeie as divs como 'flex' e adicione a seguinte propriedade no styles.css.
.flex{
display: flex;
justify-content: space-between;
}
Nosso weather.js agora ficará assim.
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">Dia: {moment().format('dddd')}</p>
<p className="day">{moment().format('LL')}</p>
</div>
<div className="flex">
<p className="temp">Temperatura: {weatherData.main.temp} °C</p>
<p className="temp">Umidade: {weatherData.main.humidity} %</p>
</div>
</div>
)
export default CardExampleCard;

Do mesmo modo, adicione os campos restantes.
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">Temperatura: {weatherData.main.temp} °C</p>
<p className="temp">Umidade: {weatherData.main.humidity} %</p>
</div>
<div className="flex">
<p className="sunrise-sunset">Nascer do sol: {new Date(weatherData.sys.sunrise * 1000).toLocaleTimeString('en-IN')}</p>
<p className="sunrise-sunset">Pôr do sol: {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;
}
Esta é a aparência da nossa aplicação agora:

Como adicionar um botão de atualizar
Vamos adicionar um botão de atualizar na parte superior da nossa página.
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">Temperatura: {weatherData.main.temp} °C</p>
<p className="temp">Umidade: {weatherData.main.humidity} %</p>
</div>
)
export default WeatherCard;
.button {
width: 35px;
height: 35px;
}

Você pode ver um botão que acionará a função de atualização. Quando você pressioná-lo, ele atualizará a página.
Como adicionar um carregador quando nossa aplicação está carregando
Vamos adicionar um carregador (em inglês, loader) para tornar a aplicação ainda mais incrível.
Importe Loader da Semantic UI e adicione-o na função de retorno, onde verificamos se há dados undefined
.
import { Dimmer, Loader } from 'semantic-ui-react';
<div className="App">
{(typeof data.main != 'undefined') ? (
<Weather weatherData={data}/>
): (
<div>
<Dimmer active>
<Loader>Carregando..</Loader>
</Dimmer>
</div>
)}
</div>
Vamos recapitular o que fizemos
Criamos uma aplicação em React que mostra o clima atual com base na sua localização.
Vamos passar por tudo que fizemos até agora.
Aprendemos sobre state e props
State e Props são recursos muito poderosos em React. Eles são usados para gerenciar dados e controlar seu fluxo dentro de diferentes componentes.
Em nossa aplicação, estamos gerenciando o state, que é o estado da aplicação. Por exemplo, o nome da cidade, a temperatura, a data, a umidade e tudo mais. Eles variam de usuário para usuário, dependendo de sua localização.
Props, por outro lado, são usadas para passar dados entre componentes. Estamos obtendo os dados no nosso arquivo app.js, mas os estamos lendo no weather.js. Lembre-se, props só podem ser usadas para passar dados do componente pai para o componente filho.
Usamos React Hooks
Se você já usou componentes de classe, deve conhecer os métodos do ciclo de vida. Se não, eles são os métodos que são chamados quando nossa página renderiza ou re-renderiza. Não podemos, porém, usar métodos do ciclo de vida em componentes funcionais, pois eles são especialmente construídos para componentes de classe.
Os React Hooks são a alternativa. Usamos dois hooks em nossa aplicação. Um é o useState
, usado para gerenciar o state da aplicação. O outro é o useEffect
, que é carregado quando a página é renderizada ou carregada.
Experimentamos a Semantic UI
A Semantic UI é uma biblioteca para React que possui componentes pré-definidos incríveis.
Isso é tudo, pessoal. Você pode adicionar mais recursos à aplicação, como uma previsão de cinco dias, ícones e muito mais.
Você pode encontrar o código no Github se quiser experimentar mais.
Você também pode assistir a este tutorial no canal do YouTube do autor (em inglês), se quiser.
Experimente sempre mais e mais. Bons estudos para você.