使数组渲染等待 axios 调用

Posted

技术标签:

【中文标题】使数组渲染等待 axios 调用【英文标题】:Making an array render wait for an axios call 【发布时间】:2021-08-31 04:40:02 【问题描述】:

我的意图是获取所选国家的天气数据,将 selectedCountry.capital 传递给查询,因此在显示国家数据时会显示当前国家首都的天气。

问题是我的代码尝试在获取天气数组之前渲染天气数据,导致错误。

TypeError: 无法读取未定义的属性“温度”

我得到数组数据


useEffect(() => 
  if( selectedCountry.capital !== '' )
  
    axios
    .get(
      `http://api.weatherstack.com/current?access_key=b51dfd70b0b2ccf136a0d7352876661c&query=$selectedCountry.capital`
    )
    .then(res =>
      console.log(res.data)
      console.log("capital" +selectedCountry.capital)
      setWeather(res.data.current)
      
     )
      
, [selectedCountry.capital])

渲染它

  <div>
          <h4>Weather</h4>
          <h5>temperature: weather.temperature Celisues</h5>
          <img src=weather.weather_icons[0] alt='' />
          <h5>
            wind: weather.wind_degree mph direction weather.wind_dir
          </h5>
        </div>   

我 如果我不渲染数组,我可以将天气数据很好地发送到控制台。另外,如果我在数组已经存在时添加数组渲染代码,天气数据会正确显示。

让数组渲染等待从 axios 调用中获取数组的最佳方法是什么?

Full code

import React,  useState, useEffect  from 'react'
import axios from 'axios'
//setCountries is a function for setting the country's state
const App = () => 
  const [countries, setCountries] = useState([])
//Filter
const [searchFilter, setSearchFilter] = useState('')

//Update state with button
const [selectedCountry, setSelectedCountry] = useState('')

const [weather, setWeather] = useState('')
  
const hook = () => 
    console.log('effect')
    axios
      .get('https://restcountries.eu/rest/v2/all')
      .then(response => 
        console.log('promise fulfilled')
        setCountries(response.data)
        
      )
  

  useEffect(hook,[])
/*   by default the effect is always run after the component has been rendered. In our case, however, we only want to execute the effect along with the first render.
  The second parameter of useEffect is used to specify how often the effect is run. If the second parameter is an empty array [], then the effect is only run along with the first render of the component. */

  console.log('render', countries.length, 'countries')
  console.log(countries)

/* weather */

useEffect(() => 
  if( selectedCountry.capital !== '' )
  
    axios
    .get(
      `http://api.weatherstack.com/current?access_key=b51dfd70b0b2ccf136a0d7352876661c&query=$selectedCountry.capital`
    )
    .then(res =>
      console.log(res.data)
      console.log("capital" +selectedCountry.capital)
      setWeather(res.data.current)
      
     )
      
, [selectedCountry.capital])





  //When button es clicked the state is set, and the state variable is used

  const renderCountryDetails = () => 
    return (
      selectedCountry && (
        <p key=selectedCountry.alpha2Code>
       <p>   Capital: selectedCountry.capital.</p>
       <p>  Population:" "
          selectedCountry.population</p> 

          <p>
            <img src=selectedCountry.flag style= width: '200px'/>
</p> 
        
<h3>Languages</h3>
<p>      selectedCountry.languages.map(language => <li key=language.name>language.name</li>)

   <div>
          <h4>Weather</h4>
          <h5>temperature: weather.temperature Celisues</h5>
          <img src=weather.weather_icons[0] alt='' />
          <h5>
            wind: weather.wind_degree mph direction weather.wind_dir
          </h5>
        </div>   

</p>



</p>


      )
    );
  ;



const filteredCountries =
searchFilter.length === 1
? countries
: countries.filter(
(country) => country.name.toLowerCase().indexOf(searchFilter.toLowerCase()) > -1
)

//showCountries returns either a message or else the contents of filteredcountries array
const showCountries = () => 


if (filteredCountries.length > 10) 
return 'Too many matches, keep on typing'



if (filteredCountries.length > 0 
    && filteredCountries.length<10 
    && filteredCountries.length>1 ) 
    
      return (
        <div>
          filteredCountries.map((country) => (
            <p key=country.alpha2Code>
              country.name
              
                //Update stste when button is clicked, passing country as a prop to the state
                //onClick state is updated, causing the page to refresh and executing renderCountryDetails
                //that uses the set state (the country) to render the info.
                <button onClick=
                  () => setSelectedCountry(country)>
                  show
                </button>
              
            </p>
          ))
          <div>renderCountryDetails()</div>
          <div>
            <p></p>
        </div>
        </div>
      );
    
    
    

    if (filteredCountries.length === 1) 
      return filteredCountries.map((country) =>

      
  <p key=country.alpha2Code>
    <p>Capital: country.capital.
    <p> Population: country.population </p> 
    <h3>languages</h3>
                country.languages.map(language => <li key=language.name>language.name</li>)

    <p><img src=country.flag style= width: '200px'/>
    </p> 
    
    </p>
    </p>

  )
      
     
     


const searchHandler = (e) => 
  //setSelectedCountry state is set to empty
  setSelectedCountry("");
setSearchFilter(e.target.value)


  return (
<div>

<div>
<h1>Countries</h1>
</div>
<div>
Type to find countries: 
<input onChange=searchHandler />
<div>
showCountries()
</div>
</div>

</div>
  );



export default App;

【问题讨论】:

无论你在哪里使用天气添加一个未定义的检查像这样&lt;h5&gt;temperature: weather &amp;&amp; weather.temperature Celisues&lt;/h5&gt; 渲染迫不及待——浏览器在此期间会做什么?数据不可用时渲染占位符,可用时渲染真实数据。使用条件,也可以使用嵌套组件。 您最好将您的应用程序拆分为几个独立的独立组件,而不是试图将所有内容都放在一个 App 中。您的方法使代码非常难以阅读。 【参考方案1】:

这里只使用可选链:

<h5>temperature: weather?.temperature||"" Celisues</h5>

在这种情况下,如果温度未定义,它不会抱怨,而是会呈现一个空字符串。

"" 可以替换为您需要在从 API 获取数据时显示为 0 或其他内容的任何默认值。

更多关于可选链接的信息:

https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Operators/Optional_chaining

【讨论】:

以上是关于使数组渲染等待 axios 调用的主要内容,如果未能解决你的问题,请参考以下文章

如何使 AsyncTask“doInBackground()”等待一个数组列表

NodeJS Express 异步/等待

长数组列表渲染使 Angular.js 中的页面滚动变慢

如何在for循环中等待每次迭代并在nodeJS中将响应作为API响应返回

如何在数组中查找数据 mongodb , nodejs

在 forEach 之后使用 Promise.all() 渲染 NodeJS 页面之前等待 Firebase 数据加载