【发布时间】:2019-05-05 14:17:56
【问题描述】:
一旦找到并显示天气,点击摄氏度应该会运行unitHandler,然后将转换温度值,然后更新状态。但是,当更新this.state.currentWeather.temp(一个已经存在的属性,所以我认为它会进行“浅”合并,并且只是“更新”状态)时,它会清除当前存在的其余状态属性。
我想知道为什么它没有像 React Docs 显示 here 的示例那样进行“浅层”合并,而是清除了我的其余状态?是不是因为 React 有时会批处理多个 setState() 调用以提高性能,如下面的文档所示?
状态更新可能是异步的 React 可能批处理多个 setState() 调用单个更新以提高性能。
因为 this.props 和 this.state 可能是异步更新的,所以你 不应依赖它们的值来计算下一个状态。
我想我只是感到困惑,因为在文档的正下方,它说在更新/合并时它将保持其他状态的其余部分完好无损:
状态更新被合并当你调用 setState() 时,React 会合并 您提供的对象进入当前状态。合并很浅,所以 this.setState({cmets}) 保持 this.state.posts 完整,但是 完全取代 this.state.cmets。
做一些研究,我认为可以防止这种情况发生的一种方法是将prevState 函数传递给this.setState,但是,我无法使用扩展运算符正确编写函数。
const root = document.querySelector('.root');
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
unit: '',
currentWeather: {
main: '',
desc: '',
temp: '',
}
}
this.getWeather = this.getWeather.bind(this);
this.unitHandler = this.unitHandler.bind(this);
}
getWeather(e) {
e.preventDefault();
const city = e.target.elements.city.value;
const country = e.target.elements.country.value;
const appID = 'bf6cdb2b4f3c1293c29610bd1d54512b';
const currentWeatherURL = `https://api.openweathermap.org/data/2.5/weather?q=${city},${country}&units=imperial&APPID=${appID}`;
const forecastURL = `https://api.openweathermap.org/data/2.5/forecast?q=${city},${country}&units=imperial&APPID=${appID}`;
//fetch CURRENT weather data ONLY
fetch(currentWeatherURL)
.then((response) => response.json())
.then((data) => {
this.setState({
unit: '°F',
currentWeather: {
main: data.weather[0].main,
desc: data.weather[0].description,
temp: data.main.temp,
}
});
})
.catch(() => {console.log('something went wrong, but we caught the error')});
}
unitHandler(e) {
function convertToCelsius(fahrenheit) {
return ((fahrenheit-32)*5/9)
}
function convertToFahrenheit(celsius) {
return ((celsius*9/5) + 32)
}
//if fahrenheit is checked
if(e.target.value === 'fahrenheit') {
const fahrenheitTemp = convertToFahrenheit(this.state.currentWeather.temp);
this.setState({unit: '°F',currentWeather: {temp: fahrenheitTemp}});
}
//otherwise, celsius is checked
else {
const celsiusTemp = convertToCelsius(this.state.currentWeather.temp);
this.setState({unit: '°C', currentWeather: {temp: celsiusTemp}});
}
}
render() {
console.log('handler state');
console.log(this.state);
return (
<div className='weather-app'>
<LocationInput getWeather={this.getWeather} unitHandler={this.unitHandler}/>
<CurrentWeather weatherStats={this.state.currentWeather} unit={this.state.unit} />
</div>
)
}
}
// Component where you enter your City and State
function LocationInput(props) {
return (
<div className='location-container'>
<form className='location-form' onSubmit={props.getWeather}>
<input type='text' name='city' placeholder='City'/>
<input type='text' name='country' placeholder='Country'/>
<button>Search</button>
<UnitConverter unitHandler={props.unitHandler} />
</form>
</div>
)
}
// Component to convert all units (fahrenheit <---> Celsius)
function UnitConverter(props) {
return (
<div className='unit-converter' onChange={props.unitHandler}>
<label for='fahrenheit'>
<input type='radio' name='unit' value='fahrenheit' defaultChecked/>
Fahrenheit
</label>
<label for='celsius'>
<input type='radio' name='unit' value='celsius'/>
Celsius
</label>
</div>
)
}
// Base weather component (intention of making specialized components for weekly forecast)
function Weather (props) {
console.log('component state');
console.log(props);
const icons = {
thunderstorm: <i class="fas fa-bolt"></i>,
drizzle: <i class="fas fa-cloud-rain"></i>,
rain: <i class="fas fa-cloud-showers-heavy"></i>,
snow: <i class="far fa-snowflake"></i>,
clear: <i class="fas fa-sun"></i>,
atmosphere: 'No Icon Available',
clouds: <i class="fas fa-cloud"></i>,
};
let currentIcon = icons[props.weatherStats.main.toLowerCase()];
return (
<div className={'weather-' + props.type}>
<h1>{props.location}</h1>
<h2>{props.day}</h2>
<figure className='weather-icon'>
<div className='weather-icon'>
{currentIcon}
</div>
<figcaption>
<h3 className='weather-main'>{props.weatherStats.main}</h3>
<div className='weather-desc'>{props.weatherStats.desc}</div>
{props.weatherStats.temp && <div className='weather-temp'>{Math.round(props.weatherStats.temp)}{props.unit}</div>}
</figcaption>
</figure>
</div>
)
}
// Using the specialization concept of React to create a more specific Weather component from base
function CurrentWeather(props) {
const dateObj = new Date();
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
const currentDay = days[dateObj.getDay()];
return (
<Weather
type={'current'}
weatherStats={props.weatherStats}
day={currentDay}
unit={props.unit}
/>
)
}
ReactDOM.render(<App />, root);
.weather-app {
text-align: center;
}
.weather-current {
display: inline-block;
}
.wf-container {
display: flex;
justify-content: center;
align-items: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div class="root"></div>
【问题讨论】:
-
请在此处添加所有相关代码,无需去外部站点查看您的尝试
-
@Icepickle 抱歉,已更新。
-
React 浅合并状态,这意味着它只查看状态对象的顶级属性,但
temp嵌套在currentWeather中,react 将完全替换。
标签: javascript reactjs