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

时间:2021-07-26 19:39:54

标签: reactjs api fetch 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>
  );
}

3 个答案:

答案 0 :(得分:5)

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

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

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

这是你重构的代码

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>
  );
}


答案 1 :(得分:0)

你可以使用另一个useEffect,这取决于改变数据状态

useEfect(() => {
  if (data) {
    // do something with data
  }
}, [data])

答案 2 :(得分:0)

您可以创建一个简单的函数并在您的 API 调用响应中调用它,然后直接从 API 响应中传入 data,这样您就可以在有响应时立即访问数据。

例如

...
.then((result) => {
  setData(result);
  getDataValue(result) // this function will be called when the response comes in and you can use the value for anything
  console.log(result);
});

方法二:

您可以使用 useEffect 钩子来监控数据状态的变化,这样每当该状态发生更新时,您就可以使用该值来做任何您想做的事情。这是我不太喜欢的选项。

useEffect(() => {
   //this hook will run whenever data changes, the initial value of data will however be what the initial value of the state is

console.log(data) //initial value = [] , next value => response from API
},[data])