首页 > 解决方案 > 重新渲染组件不会更新功能组件内的对象

问题描述

根据天气情况,应该会显示某种类型的图标。

在我的Weather组件中,我创建了一个包含所有相关图标的对象。我这样做了,而不是将其添加到state,因为根据 React:

您可以根据组件中的任何其他状态或道具来计算它吗?如果是这样,它不是状态。

并且可以根据 的值计算图标this.currentWeather.main

第一次运行时一切正常,但如果您更改城市(更改为具有不同天气类型的城市),图标将保持不变。我不知道为什么。(即尝试使用美国 Tustin ---> 美国罗切斯特)

我试过了console.log(currentIcon),我得到了一个符号对象,在它里面,它有正确的属性值,但它没有正确显示。

我的理解是,当状态更新时(通过您第二次进入另一个城市和国家),Weather组件应该重新渲染,并且return语句之前的所有代码都应该重新运行,我相信做过。

只是不确定为什么声明{currentIcon}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>

标签: javascriptreactjs

解决方案


return()Class 组件和 Functional 组件之间的区别render()总是会在返回 dom 之前重新评估其中的值,更改功能组件中的 props 可能不会返回所需的值。

你可能想试试这个:

  let currentIcon = () => icons[props.weatherStats.main.toLowerCase()]

在你的回报里面,{currentIcon}改变{currentIcon()}

您可能想考虑重新命名您的变量,例如let getWeatherIcon


推荐阅读