在 useEffect API 调用之前调用 useState 变量

lac*_*che 2 api fetch reactjs use-effect use-state

据我所知,useEffect 钩子最后作为 sideEffect 运行。我正在尝试控制台日志 data.main.temp。我可以理解它还不知道那是什么,因为它正在从后面运行的 useEffect 钩子中的 api 获取数据。

在 api 调用后,我如何能够访问或控制台日志 data.main.temp?(我觉得 setTimout 是作弊方式?)

import React, { useState, useEffect } from "react";
import Button from "../UI/Button";
import styles from "./Weather.module.css";
import moment from "moment";
import Card from "../UI/Card";

export default function Weather() {
  //State Management//
  const [lat, setLat] = useState([]);
  const [long, setLong] = useState([]);
  const [data, setData] = useState([]);

  //openWeather API key
  const key = "xxxxxxxxxxxxxxxxxxxxxxxxxx";

  useEffect(() => {
    const fetchData = async () => {
      //get coordinates//
      navigator.geolocation.getCurrentPosition(function (position) {
        setLat(position.coords.latitude);
        setLong(position.coords.longitude);
      });
      //fetch openWeather api//
      await fetch(`https://api.openweathermap.org/data/2.5/weather/?lat=${lat}&lon=${long}&units=metric&APPID=${key}`)
        .then((res) => res.json())
        .then((result) => {
          setData(result);
          console.log(result);
        });
    };
    fetchData();
  }, [lat, long]);


//Examples of what I want, they run too early before api//
console.log(data.main.temp);
const Farenheit = data.main.temp * 1.8 + 32;

  return (
    <Card>
      {typeof data.main != "undefined" ? (
        <div className={`${styles.weatherContainer} ${styles.clouds}`}>
          <h2>Weather</h2>
          <p>{data.name}</p>
          <p>{data.main.temp * 1.8 + 32} &deg;F</p>
          <p>{data.weather[0].description}</p>
          <hr></hr>
          <h2>Date</h2>
          <p>{moment().format("dddd")}</p>
          <p>{moment().format("LL")}</p>
        </div>
      ) : (
        <div></div>
      )}
    </Card>
  );
}
Run Code Online (Sandbox Code Playgroud)

小智 5

你是对的,效果函数在第一次渲染之后运行,这意味着你需要以某种方式等待你的 api 调用完成。一种常见的方法是引入另一个状态标志,指示数据是否可用。

另一件不遵循反应良好做法的事实是,您的效果函数不止一件事。

我还添加了微不足道的错误处理并清理了混合承诺和异步等待

这是你重构的代码

import React, { useState, useEffect } from "react";
import Button from "../UI/Button";
import styles from "./Weather.module.css";
import moment from "moment";
import Card from "../UI/Card";

//openWeather API key
const key = "xxxxxxxxxxxxxxxxxxxxxxxxxx";

export default function Weather() {
  //State Management//
  const [lat, setLat] = useState();
  const [long, setLong] = useState();
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    navigator.geolocation.getCurrentPosition((position) => {
      setLat(position.coords.latitude);
      setLong(position.coords.longitude);
    });
  }, []);

  useEffect(() => {
    const fetchData = async () => {
      if (lat && long && key) {
        try {
          setLoading(true);
          const response = await fetch(
            `https://api.openweathermap.org/data/2.5/weather/?lat=${lat}&lon=${long}&units=metric&APPID=${key}`
          );
          const data = await response.json();
          setData(data);
          setLoading(false);
        } catch (err) {
          setError(err);
          setLoading(false);
        }
      }
    };
    fetchData();
  }, [lat, long]);

  if (error) {
    return <div>some error occurred...</div>;
  }

  return (
    <Card>
      {loading || !data ? (
        <div>loading...</div>
      ) : (
        <div className={`${styles.weatherContainer} ${styles.clouds}`}>
          <h2>Weather</h2>
          <p>{data.name}</p>
          <p>{data.main.temp * 1.8 + 32} &deg;F</p>
          <p>{data.weather[0].description}</p>
          <hr></hr>
          <h2>Date</h2>
          <p>{moment().format("dddd")}</p>
          <p>{moment().format("LL")}</p>
        </div>
      )}
    </Card>
  );
}


Run Code Online (Sandbox Code Playgroud)