首页 > 解决方案 > 从 React 应用程序中的 api 获取数据之前的页面渲染导致错误

问题描述

我正在为导航使用以下组件。在这个导航栏上,我想显示用户当前位置的天气。唯一的问题是页面在从 openWeather api 获取数据之前正在渲染。

import React, { useState, useEffect } from 'react';

const api = {
  key: "mykey",
  base: "https://api.openweathermap.org/data/2.5/"
}


const Nav = () => {

  useEffect(() => {

    const successfulLookup = position => {
      const { latitude, longitude } = position.coords;
      fetch(`https://api.opencagedata.com/geocode/v1/json?q=${latitude}+${longitude}&key=mykey`)
        .then(response => response.json())
        .then(result => {
          const query = result.results[0].components.city;
          weatherOnLoad(query);
        })
    };

    const weatherOnLoad = query => {
      fetch(`${api.base}weather?q=${query}&units=metric&APPID=${api.key}`)
        .then(res => res.json())
        .then(result => {
          setWeather(result);
          console.log(result);
        });
    };

    // successfulLookup();

    if (window.navigator.geolocation) {
      window.navigator.geolocation
       .getCurrentPosition(successfulLookup, console.log);
    }
  }, []);


  const [weather, setWeather] = useState({});

  const dateBuilder = (d) => {
    let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

    let day = days[d.getDay()];
    let date = d.getDate();
    let month = months[d.getMonth()];
    let year = d.getFullYear();

    return `${month} ${date}, ${year} | ${day}`
  }


  return (
    <nav className="navbar navbar-expand-lg navbar-light bg-light">
      <a className="navbar-brand" href="/#">Get-Set-Note</a>
      <div className="date">{dateBuilder(new Date())}</div>
      <div className="temp">
        {Math.round(weather.main.temp)}°c
      </div>
    </nav>
  );
};

export default Nav;

因此,我收到此错误:

TypeError: Cannot read property 'temp' of undefined

如何解决这个问题?

标签: javascriptreactjsreact-nativeapi

解决方案


weather在拥有它之前不要尝试访问:

{weather && weather.main ? <div className="temp">{Math.round(weather.main.temp)}°c</div> : null}

顺便说一句,这里有一种更惯用的方式来编写代码,使用更少的嵌套函数和更多async/await来减少嵌套。

import React, { useState, useEffect } from "react";

const api = {
  key: "mykey",
  base: "https://api.openweathermap.org/data/2.5/",
};

const months = [
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December",
];
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

function formatDate(d) {
  const day = days[d.getDay()];
  const date = d.getDate();
  const month = months[d.getMonth()];
  const year = d.getFullYear();
  return `${month} ${date}, ${year} | ${day}`;
}

async function getWeatherForCoordinates(latitude, longitude) {
  const cityResp = await fetch(`https://api.opencagedata.com/geocode/v1/json?q=${latitude}+${longitude}&key=mykey`);
  const result = await cityResp.json();
  const query = result.results[0].components.city;
  const weatherResp = await fetch(`${api.base}weather?q=${query}&units=metric&APPID=${api.key}`);
  const weather = await weatherResp.json();
  return weather;
}

// Promisified `geolocation.getCurrentPosition`
async function getCurrentPositionP() {
  return new Promise((resolve, reject) => {
    if (!window.navigator.geolocation) {
      return reject("No geolocation");
    }
    window.navigator.geolocation.getCurrentPosition(resolve, reject);
  });
}

async function getLocalWeather() {
  const position = await getCurrentPositionP();
  const { latitude, longitude } = position.coords;
  const weather = await getWeatherForCoordinates(latitude, longitude);
  return weather;
}

const Nav = () => {
  const [weather, setWeather] = useState(null);
  useEffect(() => {
    getLocalWeather().then(setWeather, console.error);
  }, []);

  return (
    <nav className="navbar navbar-expand-lg navbar-light bg-light">
      <a className="navbar-brand" href="/#">
        Get-Set-Note
      </a>
      <div className="date">{formatDate(new Date())}</div>
      {weather ? <div className="temp">{Math.round(weather.main.temp)}°c</div> : null}
    </nav>
  );
};

export default Nav;

推荐阅读