Геотаргетинг определяется автоматически при условии предоставления соответствующего разрешения браузеру (и устройству).
Сходу оговорюсь: коль скоро такого рода приложение крайне несложно и многократно разрабатывалось (на любых языках программирования), сложно говорить о какой-либо эксклюзивности. Тем не менее, те пара-тройка аналогичных примеров, которые внимательно рассмотрел на гитхабе - однозначно не устроили, поэтому кодил самостоятельно. Вполне может статься, профи ReactJS доставит удовольствие попытаться найти те или иные огрехи в предлагаемом решении; ну, что же. Написано было быстро, сугубо в целях знакомства с реактом, и вполне может быть со временем отредактировано.
OK. О некоторых основах работы с create-react-app рассказано в статье Как выглядит сайт на реакте; будем считать, что вы уже создали рабочее приложение.
Понадобится доустановить три пакета:
npm install react-bootstrap bootstrap moment
Теперь создайте файл .env в корне приложения. Всего две строки:
REACT_APP_WEATHER_API_KEY = XXXXXXXXXXXXXXXXXXXX
HTTPS = true
Первая строчка определяет константу бесплатного ключа доступа к API OpenWeatherMap, что называется - зарегистрируйтесь и получите. Строчка вторая - optional, не является обязательной. Но - очень рекомендую; позволяет избежать в процессе разработки некоторых проблем, связанных с тем, что современные браузеры при работе с Geolocation API требуют защищенного соединения.
Скрипты react-app можно подробно и не торопясь рассмотреть в публичном репозитории автора, здесь же коснемся только лишь ключевого файла App.js. Который вполне способен выглядеть следующим образом:
import { useState } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import Table from "react-bootstrap/Table";
import moment from "moment";
const App = () => {
const [status, setStatus] = useState(null);
const [apiData, setApiData] = useState({});
async function fetchData(lat, lon) {
await fetch(
`//api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=metric`
)
.then((res) => res.json())
.then((data) => setApiData(data));
}
const getLocation = () => {
if (!navigator.geolocation) {
setStatus("Geolocation is not supported by your browser");
} else {
setStatus("Locating...");
navigator.geolocation.getCurrentPosition(
(position) => {
setStatus(null);
fetchData(position.coords.latitude, position.coords.longitude);
},
() => {
setStatus("Unable to retrieve your location");
}
);
}
};
const cssStyle = {
backgroundImage: "url('/background.jpg')",
height: "100vh",
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
};
return (
<div style={cssStyle}>
<button onClick={getLocation}>
Get Current Weather for Your Geo-Targeting
</button>
<p>{status}</p>
{apiData?.coord?.lat && (
<Table bordered striped hover variant="dark" size="sm">
<caption>{moment.unix(apiData?.dt).format('MMMM Do YYYY, h:mm:ss a')}</caption>
<tbody>
<tr>
<th scope="col" className="align-middle">
Average Weather
</th>
<td>
<img
src={`//openweathermap.org/img/w/${apiData?.weather?.[0].icon}.png`}
alt={apiData?.weather?.[0].main}
/>
</td>
</tr>
<tr>
<th scope="col">Area</th>
<td>{apiData?.name}</td>
</tr>
<tr>
<th scope="col">Description</th>
<td>{apiData?.weather?.[0].description}</td>
</tr>
<tr>
<th scope="col">Temperature</th>
<td>{apiData?.main?.temp} °C</td>
</tr>
<tr>
<th scope="col">Sunrise</th>
<td>{moment.unix(apiData?.sys?.sunrise).format("hh:mm a")}</td>
</tr>
</tbody>
</Table>
)}
</div>
);
};
export default App;
Показанный выше код полностью работоспособен; вы можете сменить
const App = () => {
...
на
function App() {
...
, в данном случае это не имеет значения. А вот что действительно рекомендовал бы сделать - это вынести бэкграунд vanta.js в отдельный компонент, так гораздо удобнее:
import React, { Component } from "react";
import FOG from "vanta/dist/vanta.fog.min";
import * as THREE from "three";
class FogComponent extends Component {
constructor() {
super();
this.vantaRef = React.createRef();
}
componentDidMount() {
this.vantaEffect = FOG({
el: this.vantaRef.current,
THREE: THREE,
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.0,
minWidth: 200.0,
highlightColor: 0xb6a574,
midtoneColor: 0xf5b3a9,
lowlightColor: 0xa1016,
baseColor: 0x537dcd,
blurFactor: 0.6,
speed: 5.0,
zoom: 1.5,
});
}
componentWillUnmount() {
if (this.vantaEffect) this.vantaEffect.destroy();
}
render() {
const { children } = this.props;
return (
<div style={{ height: "100vh", width: "100%" }} ref={this.vantaRef}>
{this.props.children}
</div>
);
}
}
export default FogComponent;
За подробностями см. гитхаб проекта, также отличную статью How to Pass Properties Object to Child Component.
Очень коротко и по верхам: как видите, фетчим данные погоды посредством API call, описанного в документации OpenWeatherMap; этой цели служит async function fetchData(), куда через параметры передаем значения широты и долготы. Функция вызывается из getLocation, назначение которой вполне понятно из названия: да, попросту получаем географические координаты здесь и передаем дальше. В скобках: что касается времени заката и восхода солнца - OpenWeatherMap отдает время unix, для форматирования которого нам и понадобился moment.
А getLocation, в свою очередь, инициируется по нажатию кнопки:
<button onClick={getLocation}>
Get Current Weather for Your Geo-Targeting
</button>
Дополнительно объявляем cssStyle;- думаю, не вызывает вопросов (картинка-бэкграунд). Таблица же построена с использованием optional chaining operator (?.) вот по такому принципу:
<tr>
<th scope="col">Description</th>
<td>{apiData?.weather?.[0].description}</td>
</tr>
Что в итоге получилось.
Пожалуй, на этом пока все.