无法读取未定义的属性“temp”

时间:2021-08-01 22:45:53

标签: javascript json reactjs

作为学习 React 的一个副项目,我选择制作一个天气应用。为此,我使用了 OpenWeatherMap API,并选择了 One Call API 和 Geocoding API。我遇到的问题是,当我想呈现 JSON 响应时,出现错误:TypeError: Cannot read property 'temp' of undefined。在他们的文档 (OpenWeatherMap One Call API Docs) 中,我需要用于温度的对象似乎是 current.temp,但它不起作用。

Forecast.js

function getForecast(e) {
    e.preventDefault();

    if (city.length === 0) {
        return setError(true);
    }

    // Clear state in preparation for new data
    setError(false);
    setResponseObj({});
    setLoading(true);

    const uriEncodedCity = encodeURIComponent(city);
    let KEY = process.env.REACT_APP_API_KEY
    const geoCoding = fetch(`http://api.openweathermap.org/geo/1.0/direct?q=${uriEncodedCity}&limit=1&appid=${KEY}`, {
        "method": "GET"
    })
        .then(responseGeo => responseGeo.json())

    geoCoding.then(apiCall => {
        return fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${apiCall[0].lat}&lon=${apiCall[0].lon}&units=${unit}&appid=${KEY}`)
    })
        .then(response => response.json())
        .then(response => {
            setResponseObj(response);
            setLoading(false);
        })
        .catch(err => {
            setError(true);
            setLoading(false);
            console.log(err.message);
        });
}

Conditions.js

const conditions = (props) => {
    return (
        <div className={classes.Wrapper}>
            {
                props.error && <small className={classes.Small} > Please enter a valid city. </small>}
            {
                props.loading && < div className={classes.Loader} />}

            {
                <div className={classes.information}>
                    {/* <h1 className={classes.location}><strong>{props.responseObj.name}, {props.responseObj.sys.country} </strong></h1>
                        <p className={classes.title} ><img className="card-img-top" src={`http://openweathermap.org/img/wn/${props.responseObj.weather[0].icon}@2x.png`} alt="weather icon" style={{ width: 130, height: 130 }} /></p> */}
                    <p className={classes.title} >{Math.round(props.responseObj.current.temp)}° with {props.responseObj.current.weather[0].description}. </p>
                    {<p className={classes.info}>{clothes(props.unit, Math.round(props.responseObj.current.temp))}</p>}
                    <p className={classes.info}>{rain(props.responseObj.current.weather[0].description)}</p>
                    <p className={classes.info}><img src={Thermo} alt="feels like icon" style={{ width: 50, height: 50 }} /> Feels like: <b>{(props.responseObj.current.feels_like).toFixed()}° </b></p>
                    <p className={classes.info}><img src={Atmoshperic} alt="atmospheric icon" style={{ width: 50, marginRight: 5, height: 50 }} /> Atmoshperic Pressure: <b>{props.responseObj.current.pressure}hPa</b></p>
                    <p className={classes.info}><img src={Humidity} alt="humidity icon" style={{ width: 50, marginRight: 5, height: 50 }} /> Humidity: <b>{props.responseObj.current.humidity}%</b></p>
                    <p className={classes.info}><img src={Cloudy} alt="cloudy icon" style={{ width: 50, height: 50 }} /> Cloudiness: <b>{(props.responseObj.current.clouds)}% </b></p>
                    <p className={classes.info}><img src={Wind} alt="wind icon" style={{ width: 50, marginRight: 5, height: 50 }} /> Wind: <b>{props.responseObj.current.wind_speed} m/s, {compassSector[(props.responseObj.current.wind_deg / 22.5).toFixed(0)]} </b> <img src={Arrow} alt="windarrow" style={{ width: 14, transform: `rotate(${props.responseObj.current.wind_deg}deg)`, height: 14 }} /></p>
                    <p className={classes.info}><img src={Sunrise} alt="sunrise icon" style={{ width: 50, marginRight: 5, height: 50 }} /> Sunrise is at: <b>{moment.unix(props.responseObj.current.sunrise).format('HH:mm')} </b></p>
                    <p className={classes.info}> <img src={Sunset} alt="sunset icon" style={{ width: 50, marginRight: 5, height: 50 }} />Sunset is at: <b>{moment.unix(props.responseObj.current.sunset).format('HH:mm')}</b></p>
                </div >
            } </div>
    )
}

编辑:

我不明白的是为什么 JSON 响应中的某些字段可以是渲染没有任何问题,有些则不能。

示例:

{
   "lat":47.1667,
   "lon":27.6,
   "timezone":"Europe/Bucharest",
   "timezone_offset":10800,
   "current":{
      "dt":1627819113,
      "sunrise":1627786102,
      "sunset":1627839801,
      "temp":86.52,
      ...
}

如果我使用 props.responseObj.latprops.responseObj.lon,我不会出错,但如果我尝试使用 props.responseObj.current.temp,我会收到 TypeError: Cannot read property 'temp' of undefined 错误。

1 个答案:

答案 0 :(得分:2)

问题

看起来您正在访问初始渲染时未定义的属性。

  1. Forecast 中,初始 responseObj 状态为空对象,而 loading 状态初始为假。
  2. {} responseObj 值被传递给 Conditions
  3. Conditions 尝试访问 props.responseObj.current.XXXX

props 是 OFC 定义的,props.responseObj 也是如此,值为 {}。下一段 props.responseObj.current 未定义。在您尝试访问未定义值的 temp 之前,这本身并不是问题。

注意:这可能在调用 getForecast 的任何时候发生,因为它会将 responseObj 状态重置回空对象 ({})。

解决方案

如果 props.responseObj 属性存在,您应该有条件地呈现 current 数据。

const conditions = (props) => {
  return (
    <div className={classes.Wrapper}>
      {props.error && (
        <small className={classes.Small}> Please enter a valid city. </small>
      )}

      {props.loading && <div className={classes.Loader} />}

      {props.responseObj.current && (
        <div className={classes.information}>
          <p className={classes.title}>
            {Math.round(props.responseObj.current.temp)}° with{" "}
            {props.responseObj.current.weather[0].description}.{" "}
          </p>
          {
            <p className={classes.info}>
              {clothes(props.unit, Math.round(props.responseObj.current.temp))}
            </p>
          }
          <p className={classes.info}>
            {rain(props.responseObj.current.weather[0].description)}
          </p>
          <p className={classes.info}>
            <img
              src={Thermo}
              alt="feels like icon"
              style={{ width: 50, height: 50 }}
            />{" "}
            Feels like:{" "}
            <b>{props.responseObj.current.feels_like.toFixed()}° </b>
          </p>
          <p className={classes.info}>
            <img
              src={Atmoshperic}
              alt="atmospheric icon"
              style={{ width: 50, marginRight: 5, height: 50 }}
            />{" "}
            Atmoshperic Pressure: <b>{props.responseObj.current.pressure}hPa</b>
          </p>
          <p className={classes.info}>
            <img
              src={Humidity}
              alt="humidity icon"
              style={{ width: 50, marginRight: 5, height: 50 }}
            />{" "}
            Humidity: <b>{props.responseObj.current.humidity}%</b>
          </p>
          <p className={classes.info}>
            <img
              src={Cloudy}
              alt="cloudy icon"
              style={{ width: 50, height: 50 }}
            />{" "}
            Cloudiness: <b>{props.responseObj.current.clouds}% </b>
          </p>
          <p className={classes.info}>
            <img
              src={Wind}
              alt="wind icon"
              style={{ width: 50, marginRight: 5, height: 50 }}
            />{" "}
            Wind:{" "}
            <b>
              {props.responseObj.current.wind_speed} m/s,{" "}
              {
                compassSector[
                  (props.responseObj.current.wind_deg / 22.5).toFixed(0)
                ]
              }{" "}
            </b>{" "}
            <img
              src={Arrow}
              alt="windarrow"
              style={{
                width: 14,
                transform: `rotate(${props.responseObj.current.wind_deg}deg)`,
                height: 14
              }}
            />
          </p>
          <p className={classes.info}>
            <img
              src={Sunrise}
              alt="sunrise icon"
              style={{ width: 50, marginRight: 5, height: 50 }}
            />{" "}
            Sunrise is at:{" "}
            <b>
              {moment.unix(props.responseObj.current.sunrise).format("HH:mm")}{" "}
            </b>
          </p>
          <p className={classes.info}>
            {" "}
            <img
              src={Sunset}
              alt="sunset icon"
              style={{ width: 50, marginRight: 5, height: 50 }}
            />
            Sunset is at:{" "}
            <b>
              {moment.unix(props.responseObj.current.sunset).format("HH:mm")}
            </b>
          </p>
        </div>
      )}
    </div>
  );
};

您还可以对加载状态使用三元运算符。

{props.loading ? (
  <div className={classes.Loader} />
) : (
  <div className={classes.Wrapper}>
    .....
  </div>
)}

或者在所有访问中使用可选链操作符。

const conditions = (props) => {
  return (
    <div className={classes.Wrapper}>
      {props.error && (
        <small className={classes.Small}> Please enter a valid city. </small>
      )}
      
      {props.loading && <div className={classes.Loader} />}

      <div className={classes.information}>
        <p className={classes.title}>
          {Math.round(props.responseObj.current?.temp)}° with{" "} // <-- Optional Chaining
          {props.responseObj.current?.weather[0].description}.{" "} // <-- Optional Chaining
        </p>
        {
          <p className={classes.info}>
            {clothes(props.unit, Math.round(props.responseObj.current?.temp))} // <-- Optional Chaining
          </p>
        }
        <p className={classes.info}>
          {rain(props.responseObj.current?.weather[0].description)} // <-- Optional Chaining
        </p>
        ...... etc....
      </div>
    </div>
  );
};