繁体   English   中英

从子组件调用时未设置 setState

[英]setState not setting when called from child component

我有一个简单的应用程序,它获取一些天气 JSON 并显示它。 用户可以输入一个位置,也可以点击“获得幸运”按钮,该按钮会随机获取一个城市。 初始状态在 App.js 中设置

    this.state = {
      error: '',
      status: '',
      queryString: 'london,gb',
      queryID: '',
      queryType: 'q',
      cityData: cityData, 
      weatherData: {},
      isLoaded: false
    }

接下来,我有我的主 App 类,然后我有一个包含表单 gubbins 的子组件。 我在应用程序渲染中调用它如下:

      <SearchForm
        queryString={this.state.queryString}
        handleChange={this.handleChange}
        setQueryType={this.setQueryType}
        setQueryID={this.setQueryID}
        getWeatherData={this.getWeatherData}
      />

我在那里使用回调函数来设置查询类型(位置或 ID)。 App.js 中回调函数之一的示例是:

  setQueryType = (queryType) => {
    this.setState({
      queryType: queryType
    })
  }

这在表单 JS 中使用:

 props.setQueryType(e.target.attributes.query.value)

现在,问题的关键在于:状态不会在第一次更新,但在第二次点击时会更新? 事实上,在 fetch 中设置的其他变量,如 queryString 直到第二次点击才会设置。

应用程序.js

import React, { Component } from 'react';
import './css/App.css';
import WeatherCard from './components/WeatherCard'
import Header from './components/Header'
import SearchForm from './components/SearchForm'
import cityData from './json/city.list'

const config = {
  API: 'https://api.openweathermap.org/data/2.5/forecast',
  API_KEY: process.env.REACT_APP_OPEN_WEATHER_MAP_API_KEY
}

class App extends Component {

  constructor() {
    super()
    this.state = {
      error: '',
      status: '',
      queryString: 'london,gb',
      queryID: '',
      queryType: 'q',
      cityData: cityData, 
      weatherData: {},
      isLoaded: false
    }

    this.getWeatherData()
  }

  getWeatherData = (searchValue="london,gb") => {
    let URL
    URL = config.API + '?' + this.state.queryType + '='
    URL += this.state.queryType === 'q' ? searchValue : this.state.queryID
    URL += '&units=metric&APPID=' + config.API_KEY

    console.log(URL)

    fetch(URL)
      .then( result => result.json() )
      .then ( 
        (result) => {
          if ( result.cod === '200') {
            this.setState({ 
              status: result.cod,
              weatherData: result,
              queryString: result.city.name,
              isLoaded: true
            })
          } else {
            this.setState({
              status: result.cod,
              error: result.message,
              isLoaded: false
            })
          }
      },
      (error) => {
        this.setState({
          isLoaded: false,
          error: error
        })
      }
    )
    console.log(this.state.queryString)
  }

  handleChange = (event) => {
    const { name, value } = event.target
    this.setState({
      [name]: value
    })
  }

  getWeatherCards = () => {
    let cards = []
    for (let i = 0; i < this.state.weatherData.cnt; i++) {
      cards.push(
        <WeatherCard 
          key={i} 
          weatherList={this.state.weatherData.list[i]} 
        />
      )
    }
    return cards
  }

  setQueryType = (queryType) => {
    this.setState({
      queryType: queryType
    })
  }

  setQueryID = () => {
    let randomID = Math.floor(Math.random() * this.state.cityData.length)
    let randomCityID = this.state.cityData[randomID].id

    this.setState({
      queryID: randomCityID
    })
  }

  getlocationForm = () => {
    return(
      <SearchForm
        queryString={this.state.queryString}
        handleChange={this.handleChange}
        setQueryType={this.setQueryType}
        setQueryID={this.setQueryID}
        getWeatherData={this.getWeatherData}
      />
    )
  }

  render = () => {
    if (this.state.status !== '200') {
      return (
        <div className='App'>
          <Header 
            status={this.state.status}
            error={this.state.error}
          />
          {this.getlocationForm()}
        </div>
      )
    } else {
      return (
        <div className='App'>
          {
            this.state.isLoaded && (
              <Header 
                cityName={this.state.weatherData.city.name} 
                countryName={this.state.weatherData.city.country} 
                status={this.state.status}
                error={this.state.error}
              />
            )
          }
          {this.getlocationForm()}
          {
            this.state.isLoaded && (
              <div className='weather-cards'>
                {this.getWeatherCards()}
              </div>
            )
          }
        </div>
      )
    }
  }
}

export default App;

搜索表单.js

import React from 'react'

const SearchForm = (props) => {

  let handleChange = function(e) {
    props.handleChange(e)
  }

  let handleClick = function(e) {
    e.preventDefault()

    props.setQueryType(e.target.attributes.query.value)

    if (e.target.attributes.query.value === 'id') { 
      props.setQueryID()
    } 

    props.getWeatherData()
  }

  return (
    <div>
      <form className="search-form">
        <input 
          type="text" 
          id="query"
          name="query" 
          placeholder="Enter a location..."
          onChange={handleChange} 
        /> 
        <button 
          type="submit" 
          query="q" 
          onClick={handleClick}
        >
          Submit
        </button>
        <button 
          type="submit" 
          query="id" 
          onClick={handleClick}
        >
          I'm feeling lucky...
        </button>
      </form>
    </div>
  )
}

export default SearchForm

在你的App.js构造函数中添加this.setQueryType = this.setQueryType.bind(this)

该行将this的上下文绑定到当前组件,因此当从子组件调用时,将更新父状态。

尝试将您的 this.getWeatherData() 放入 componentDidMount 并将其从构造函数中删除

componentDidMount() {
   this.getWeatherData()
}

我认为问题来自这样一个事实,即当您调用getWeatherData ,您不知道setState是否会结束,因为它是一个异步方法。 (如您在文档中所见)

因此,在不确定组件状态的情况下,确保在调用方法之前完成setState的最佳方法是使用 setState 的 callBack 参数来确保它在setState方法完成后运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM