根据天气情况,应该会出现某种类型的图标。
在我的Weather组件中,我创建了一个包含所有相关图标的对象。我这样做了,而不是将它添加到state中,因为根据React:
您能根据组件中的任何其他状态或道具计算它吗?如果是这样的话,那就不是国家。
并且可以根据this.currentWeather.main的值来计算图标。
在第一次运行时,一切都很好,但是如果您更改了城市(更改为不同天气类型的城市),图标将保持不变。我搞不懂为什么。(即)试着使用美国的塔斯丁-美国罗切斯特
我尝试过console.log(currentIcon),我得到了一个符号对象,在它里面,它有正确的属性值,但是它没有正确的显示。
我的理解是,当状态更新时(通过第二次进入另一个城市和国家),应该重新呈现Weather组件,return语句之前的所有代码都应该被重新运行,我认为应该是这样的。
只是不确定为什么return语句中的return没有反映这种变化。
我很想知道答案,但是我很想知道为什么显示没有被更新。
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(prevState => ({
unit: '°F',
currentWeather: {
...prevState.currentWeather,
temp: fahrenheitTemp,
}
}));
}
//otherwise, celsius is checked
else {
const celsiusTemp = convertToCelsius(this.state.currentWeather.temp);
this.setState(prevState => ({
unit: '°C',
currentWeather: {
...prevState.currentWeather,
temp: celsiusTemp,
}
}));
}
}
render() {
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) {
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()];
console.log(currentIcon);
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://use.fontawesome.com/releases/v5.5.0/js/all.js"></script>
<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>
发布于 2018-12-05 08:13:28
类组件中的return()与函数式组件中的render()之间的区别在于,在返回dom之前,render()总是会重新计算其中的值,而在函数组件中更改道具可能不会返回所需的值。
你可能想试试这个:
let currentIcon = () => icons[props.weatherStats.main.toLowerCase()]在您的返回中,用{currentIcon}更改{currentIcon()}
您可能需要考虑重新命名变量,例如let getWeatherIcon。
发布于 2018-12-05 07:27:52
我想我看到你的问题了,试试以下几点:
const icons = {
thunderstorm: () => <i class="fas fa-bolt"></i>,
};将该对象中的每个图标更改为带有返回的函数。
希望这能有所帮助
劳埃德
发布于 2018-12-05 09:53:09
Fontawesome库似乎做了一些DOM操作来用svg替换您的图标元素--它在第一个页面呈现时工作得很好,但是当您开始尝试用React更新DOM节点时,Fontawesome不知道它应该用新的svg路径替换那个图标。
这样的DOM操作(当React不知道这些操作时)可能会导致这样的错误
"react DOMException:未能在‘node’上执行'removeChild‘:要删除的节点不是该节点的子节点。“
有时它们相当乏味,很难调试。
您所需要做的就是将依赖关系从
"https://use.fontawesome.com/releases/v5.5.0/js/all.js"至
"https://use.fontawesome.com/releases/v5.5.0/css/all.css"后者将使用静态字体图标符号,而不需要过多的DOM更新,这样一切都会正常工作。
我在您的代码中编辑了几个排版,您可以查看应用程序的完整工作示例:
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(prevState => ({
unit: "°F",
currentWeather: {
...prevState.currentWeather,
temp: fahrenheitTemp
}
}));
}
//otherwise, celsius is checked
else {
const celsiusTemp = convertToCelsius(this.state.currentWeather.temp);
this.setState(prevState => ({
unit: "°C",
currentWeather: {
...prevState.currentWeather,
temp: celsiusTemp
}
}));
}
}
render() {
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) {
const icons = {
thunderstorm: <i className="fas fa-bolt" />,
drizzle: <i className="fas fa-cloud-rain" />,
rain: <i className="fas fa-cloud-showers-heavy" />,
snow: <i className="far fa-snowflake" />,
clear: <i className="fas fa-sun" />,
atmosphere: "No Icon Available",
clouds: <i className="fas fa-cloud" />,
mist: "No Icon Available"
};
const currentIcon = icons[props.weatherStats.main.toLowerCase()];
return (
<div className={"weather-common"}>
<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}
/>
);
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);.App {
font-family: sans-serif;
text-align: center;
}
.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>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.5.0/css/all.css"
integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU"
crossorigin="anonymous"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
https://stackoverflow.com/questions/53626974
复制相似问题