在 useEffect API 调用之前调用 useState 变量
Posted
技术标签:
【中文标题】在 useEffect API 调用之前调用 useState 变量【英文标题】:useState variable is called before useEffect API call 【发布时间】:2021-10-02 17:28:43 【问题描述】:据我了解,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 °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>
);
【问题讨论】:
【参考方案1】:你是对的,效果函数在第一次渲染之后运行,这意味着你需要以某种方式等待,直到你的 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 °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>
);
【讨论】:
我明白了,非常彻底。我很感激花时间重构代码,我不知道如何使用单独的 useEffects! 有一个问题,如果我是对的,第一个 useEffect 只会运行一次,而且由于用户的 lcoation 可能会改变,我不需要一个数组依赖项吗?Math.round(parseFloat(data.main.temp) * 1.8 + 32) °F
顺便说一句,如果有人需要在这里转换为 Farenhiet去 是的,空的依赖数组意味着效果只会运行一次(挂载时)。 getCurrentPosition API 是一种“一次性”操作,因此将其放入“onMount”挂钩是有意义的。但是,如果您想查看用户位置,则需要在钩子内使用navigator.geolocation.watchPosition
API。这个 API 让你订阅位置变化。但需要注意的重要一点是,您必须使用效果清理(从效果返回的函数)才能取消注册处理程序return () => navigator.geolocation.clearWatch(id);
【参考方案2】:
你可以使用另一个useEffect,这取决于改变数据状态
useEfect(() =>
if (data)
// do something with data
, [data])
【讨论】:
【参考方案3】:您可以创建一个简单的函数并在您的 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);
);
方法2:
您可以使用 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])
【讨论】:
谢谢!我确实喜欢选项一的概念是的以上是关于在 useEffect API 调用之前调用 useState 变量的主要内容,如果未能解决你的问题,请参考以下文章
React - 是不是在 useEffect 中调用 API
定期为 Dashboard API 调用运行 useEffect
如何阻止多个重新渲染执行多个 api 调用 useEffect?