Я использую условный рендеринг для моего погодного приложения на github. Приложение работает нормально, но когда оно отображает результат для вновь введенного города, расположение макета похоже на дрожание, пока результат / прогноз погоды для нового города не будет загружен. Я полагаю, это вопрос дизайна. Может кто-нибудь проверить?
Вот мое приложение:
import React, { Component } from 'react';
import Spinner from '../UI/Spinner';
import './App.css';
import {TweenLite, TimelineLite} from "gsap/TweenMax";
class App extends Component {
state = {
cityData: null,
cityName: '',
confirmedCityName: '',
error: '',
searchNameString: '',
woeid: '',
weatherData: '',
weatherForecast: '',
weatherIcon: null,
loading: false
}
validate = () => {
let isError = false;
let error = '';
if(this.state.cityName.length === 0){
isError = true;
error = 'Pls.enter a valid city name';
} else {
error = ''
}
this.setState({
error,
confirmedCityName: '',
})
return isError
}
submitHandler = (event) => {
event.preventDefault();
const err = this.validate();
if(err === false) {
const searchString = 'https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/search/?query=';
const searchNameString = searchString.concat(this.state.cityName);
this.setState({
searchNameString
})
fetch(searchNameString)
.then(response => {
console.log(response);
return response.json()
})
.then(data => {
if(data.length === 0) {
let error = 'You have entered invalid city name or name that not exist in our database of cities, pls.enter existent city name';
this.setState({
error,
confirmedCityName: ''
})
} else {
const woeid = data[0].woeid;
this.setState({
loading: true,
confirmedCityName: this.state.cityName,
cityData: data,
woeid
})
const searchString = 'https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/';
const searchWoeidString = searchString.concat(`${this.state.woeid}/`);
return fetch(searchWoeidString)
}
})
.then(response => {
return response.json()
}
)
.then(data => {
const today = data.consolidated_weather[0];
const country = data.parent.title;
const weatherForecast =
<div>Today in <strong>{this.state.cityName}, {country}</strong> weather will be with <strong>{today.weather_state_name}</strong>,
min temperature <strong>{parseInt(today.min_temp)}°C</strong>,
max temperature <strong>{parseInt(today.max_temp)}°C</strong>,
and humidity will be <strong>{today.humidity}%</strong></div>;
const weatherIcon = `https://www.metaweather.com/static/img/weather/png/${today.weather_state_abbr}.png`;
this.setState({
loading: false,
weatherData: data,
weatherForecast,
weatherIcon,
cityName: ''
})
})
.catch(error => console.log(error));
this.setState({
weatherData: '',
weatherForecast: '',
})
}
}
nameChangeHandler = input => event => {
this.setState({
[input]: event.target.value
})
}
componentDidMount(){
let animation = new TimelineLite();
TweenLite.ticker.useRAF(false);
TweenLite.lagSmoothing(0);
const spinningPic = document.getElementById('spinning-pic');
animation.to(spinningPic, 1, {rotation: "+=360", transformOrigin:"center center"});
}
render() {
let weatherOutput = null;
if(this.state.loading) {
weatherOutput = (
<React.Fragment>
{/*Spinner*/}
<Spinner />
{/*Content-forecast-empty*/}
<div className="container-fluid content-forecast">
<div className="container">
<p className="forecast"></p>
</div>
</div>
</React.Fragment>
)
} else {
weatherOutput = (
<React.Fragment>
{/*Weather Icon got from server*/}
<div className={"container-fluid content" + (this.state.weatherIcon !== null ? ' visible' : ' nonvisible')}>
<div className="container">
<div className="img-wrapper"><img src={this.state.weatherIcon} alt=""/></div>
</div>
</div>
{/*Forecast string empty*/}
<div className="container-fluid content-forecast">
<div className="container">
<div className="forecast">{this.state.weatherForecast}</div>
</div>
</div>
</React.Fragment>
)
}
return (
<div className="App">
<div className="container-fluid title-background">
<div className="container">
<h1>Weather forecast app</h1>
</div>
</div>
<div className="container-fluid header-background">
<div className="container">
<h3>Search Weather data for {this.state.confirmedCityName}</h3>
<form>
<input
type="text"
placeholder="Pls.enter city name"
value={this.state.cityName}
onChange={this.nameChangeHandler('cityName')}
/>
<button
type="submit"
onClick={this.submitHandler}
>
ENTER
</button>
</form>
<p className="error-message">{this.state.error}</p>
</div>
</div>
<div className="container-fluid">
{/*Opening pic*/}
<div className="container">
<div className="spinning-pic-wrapper"><img id="spinning-pic" src="https://www.metaweather.com/static/img/weather/png/c.png"
className={(this.state.weatherIcon !== null || this.state.loading === true ? 'nonVisible' : '')} alt="sun"/></div>
</div>
{/*Weather Icon got from server, Forecast string empty*/}
<div className="weatherOutputWrapper">
{weatherOutput}
</div>
</div>
<div className="container-fluid footer-background">
<div className="container">
<h3>Getting today's weather forecast</h3>
<div className="icon-wrapper"><img src="https://www.metaweather.com/static/img/weather/lc.svg" alt="weather Icon"/></div>
</div>
</div>
</div>
);
}
}
export default App;
Вот файл CSS:
/* ------------------------------------ */
input {
outline: none;
border: none;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 15px;
margin-right: 10px;
font-size: 18px;
border-radius: 20px;
}
/* ------------------------------------ */
button {
outline: none;
border: solid 2px #fff;
color: #fff;
border-radius: 20px;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 15px;
padding-right: 15px;
margin-bottom: 15px;
background: transparent;
display: inline-block;
text-align: center;
transition: background-color .3s ease-in-out, border .3s ease-in-out;
}
button:hover, button:focus {
cursor: pointer;
background-color: #00697D;
border: solid 2px transparent;
}
img {
max-width: 100%;
height: auto;
max-height: 450px;
margin-top: 25px;
display: inline-block;
}
h1 {
font-family: OswaldLight;
font-size: 25px;
padding-top: 10px;
padding-bottom: 10px;
}
form {
text-align: center;
}
.container-fluid {
padding-right: 0px;
padding-left: 0px;
margin-right: auto;
margin-left: auto;
}
.container {
padding-right: 0px;
padding-left: 0px;
margin-right: auto;
margin-left: auto;
max-width: 100%;
}
.title-background {
background-color: #353432;
border-bottom: 1px solid #666;
}
.title-background h1 {
color: #fff;
}
.header-background {
background-color: #4E4D4A;
}
.header-background h3 {
font-size: 35px;
padding-top: 30px;
padding-bottom: 10px;
text-align: center;
background-color: #4E4D4A;
color: #FF8C18;
}
.header-background p {
text-align: center;
}
.content {
text-align: center;
display: none;
}
.content-forecast {
height: 45px;
padding-top: 15px;
padding-bottom: 15px;
margin-top: 40px;
background-color: #D3D3D3;
text-align: center;
}
.weather-forecast {
background-color: #4E4D4A;
}
.error-message {
color: #ecf0f1;
height: 20px;
padding-bottom: 35px;
}
.spinning-pic-wrapper {
text-align: center;
}
#spinningPic {
transform-origin: 50% 50%;
}
.nonVisible {
display: none;
}
.visible {
display: block;
}
.footer-background {
background-color: #353432;
border-top: 1px solid #666;
font-family: OswaldLight;
font-size: 18px;
padding-top: 10px;
padding-bottom: 10px;
text-align: right;
}
.footer-background h3 {
color: #fff;
display: inline-block;
margin-right: 10px;
}
.icon-wrapper {
display: inline-block;
height: 25px;
width: 25px;
}
.icon-wrapper img{
margin-top: 0px;
}