Вопрос по state React
У меня есть поле ввода в которое я ввожу название города и после этого получаю координаты города и прогноз погоды. Для этого я создал функциональную компоненту Content в которой с помощью библиотеки axios получаю ответы от сервера. Изначально у меня установлено 2 состояния компонента:
const [coordinates, setCoordinates] = useState([{
lat: 0,
lng: 0
}]);
const [forecast, setForecast] = useState([{
dt: 0,
day: 0,
max: 0,
min: 0,
eve: 0,
morning: 0,
night: 0,
main: '',
description: '',
icon: '',
sunrise: 0,
sunset: 0
}]);
После того, как я получаю из поля ввода название города я отправляю GET запрос по ссылке:
https://maps.googleapis.com/maps/api/geocode/json?address=${YOUR_CITY_NAME}&key=${YOUR_TOKEN}
И получаю координаты города: lat и lng соответственно. И дальше я пытаюсь запихнуть их в стейт coordinates.
После этого делаю запрос, но уже на сервер Open Weather Map:
https://api.openweathermap.org/data/2.5/onecalllat=${coordinates.lat}&lon=${coordinates.lng}&exclude=hourly,minutely&appid=${YOUR_TOKEN}
Проблема в том, что при первом запросе у меня в coordinates.lat и coordinates.lng дефолтное значени, т.е 0. Почему-то новый стейт присваивается только после второго нажатия на кнопку вызова функции. И, если, я например введу в инпут "Киев", то получу погоду по координатам lat = 0, lng = 0 и после этого введу в инпут "Москва", то получу погоду по координатам киева, т.е прошлый стейт. Как это исправить? Вот весь код:
import React from 'react';
import { useState } from 'react';
import Forecast from './Forecast';
import '../style/Content.css';
const axios = require('axios').default;
const moment = require('moment');
const Content = () => {
const [coordinates, setCoordinates] = useState([{
lat: 0,
lng: 0
}]);
const [forecast, setForecast] = useState([{
dt: 0,
day: 0,
max: 0,
min: 0,
eve: 0,
morning: 0,
night: 0,
main: '',
description: '',
icon: '',
sunrise: 0,
sunset: 0
}]);
let inputValue = React.createRef();
const getCityName = () => inputValue.current.value;
const getForecastData = async () => {
axios.get(`https://maps.googleapis.com/maps/api/geocode/json?address=${getCityName()}&key=${YOUR_TOKEN}`)
.then(async (res) => {
const newCoordinates = {
lat: await res.data.results[0].geometry.location.lat,
lng: await res.data.results[0].geometry.location.lng
}
console.log(newCoordinates.lat, newCoordinates.lng);
setCoordinates(newCoordinates);
console.log(coordinates);
})
.catch(err => {
console.error(err);
});
setTimeout(() => {
axios.get(`https://api.openweathermap.org/data/2.5/onecall?lat=${coordinates.lat}&lon=${coordinates.lng}&exclude=hourly,minutely&appid=${YOUR_TOKEN}`)
.then(res => {
console.log(res);
const newWeather = {
day: Math.floor(res.data.daily[0].temp.day - 273),
max: Math.floor(res.data.daily[0].temp.max - 273),
min: Math.floor(res.data.daily[0].temp.min - 273),
eve: Math.floor(res.data.daily[0].temp.eve - 273),
morning: Math.floor(res.data.daily[0].temp.morn - 273),
night: Math.floor(res.data.daily[0].temp.night - 273),
main: res.data.daily[0].weather[0].main,
description: res.data.daily[0].weather[0].description,
icon: res.data.daily[0].weather[0].icon,
dt: moment(res.data.daily[0].dt, 'X').format('dddd, DD MMM'),
sunrise: moment(res.data.daily[0].sunrise, 'X').format('dddd, HH:mm'),
sunset: moment(res.data.daily[0].sunset, 'X').format('dddd, HH:mm')
}
setForecast(newWeather);
console.log(forecast);
})
.catch(err => {
console.error(err);
})
}, 500);
}
return (
<div className='content'>
<div>
<input type='text' ref={inputValue} placeholder='City name'></input>
<button className="btn" onClick={getForecastData}>Go</button>
</div>
<Forecast />
</div>
);
}
export default Content;
Ответы (1 шт):
Давайте разберем, что делает ваш код:
- Вы объявили стейты.
- Получаем доступ к INPUT в рендере через REF
- Пытаетесь получить значение INPUT
- Создали функцию с получением данных.
А теперь какие ошибки увидел я:
- В стейт вы кладете массив в котором лежит один объект с координатами. Я бы убрал массивы, у вас поиск по одному объекту. А так же не понимаю для чего вам стейт для координат, после изменения стейта произойдет новый рендер, но сам компонент остался без изменений.
const [forecast, setForecast] = useState({
dt: 0,
day: 0,
max: 0,
min: 0,
eve: 0,
morning: 0,
night: 0,
main: '',
description: '',
icon: '',
sunrise: 0,
sunset: 0
});
Вы сразу хотите получить значение из Input, но зачем? компонент только появился, а вы из него уже считываете значение которое будет пустое.
При получение данных вы используете setTimeout в 500мс, я так понимаю вы делаете задержку между получением первых данных и вторых. Но если первый запрос будет дольше 500мс, то второй запрос отработает с ошибкой. Почитайте про async/await Promise подробнее.
const getForecastData = async() => {
try {
const getCityName = () => inputValue.current.value;
let getCoordinates = await axios.get(`https://maps.googleapis.com/maps/api/geocode/json?address=${getCityName()}&key=${YOUR_TOKEN}`).then(res => res.data.results[0].geometry.location);
const newCoordinates = {
lat: getCoordinates.lat,
lng: getCoordinates.lng
}
let getForecast = await axios.get(`https://api.openweathermap.org/data/2.5/onecall?lat=${coordinates.lat}&lon=${coordinates.lng}&exclude=hourly,minutely&appid=${YOUR_TOKEN}`).then(res => res.data.daily[0]);
const newWeather = {
day: Math.floor(getForecast.temp.day - 273),
max: Math.floor(getForecast.temp.max - 273),
min: Math.floor(getForecast.temp.min - 273),
eve: Math.floor(getForecast.temp.eve - 273),
morning: Math.floor(getForecast.temp.morn - 273),
night: Math.floor(getForecast.temp.night - 273),
main: getForecast.weather[0].main,
description: getForecast.weather[0].description,
icon: getForecast.weather[0].icon,
dt: moment(getForecast.dt, 'X').format('dddd, DD MMM'),
sunrise: moment(getForecast.sunrise, 'X').format('dddd, HH:mm'),
sunset: moment(getForecast.sunset, 'X').format('dddd, HH:mm')
}
setForecast(newWeather);
} catch (err) {
console.log(err);
}
}