如何在 React 的 UseEffect() 中调用异步函数?
Posted
技术标签:
【中文标题】如何在 React 的 UseEffect() 中调用异步函数?【英文标题】:How to call an async function inside a UseEffect() in React? 【发布时间】:2019-11-12 06:30:07 【问题描述】:我想调用一个异步函数并为我的 UseEffect 获取结果。
我在网上找到的 fetch api 示例是直接在 useEffect 函数中制作的。 如果我的 URL 发生变化,我必须修补我的所有获取。
当我尝试时,我收到一条错误消息。
这是我的代码。
async function getData(userId)
const data = await axios.get(`http://url/api/data/$userId`)
.then(promise =>
return promise.data;
)
.catch(e =>
console.error(e);
)
return data;
function blabla()
const [data, setData] = useState(null);
useEffect(async () =>
setData(getData(1))
, []);
return (
<div>
this is the data["name"]
</div>
);
index.js:1375 警告:效果函数不能返回除用于清理的函数之外的任何内容。 看起来你写了 useEffect(async () => ...) 或者返回了一个 Promise。相反,在你的效果中编写异步函数并立即调用它:
useEffect(() =>
async function fetchData()
// You can await here
const response = await MyAPI.getData(someId);
// ...
fetchData();
, [someId]); // Or [] if effect doesn't need props or state
【问题讨论】:
如果一个函数返回一个promise,你可以await
或者.then(...)
,不能同时使用。
这回答了我猜想的问题,但这并没有真正解决问题。你实际上是在开始一个可能随时结束的承诺。如果您的异步作业与组件的生命周期无关,那很好。但否则,您将首先遇到麻烦并且难以调试渲染错误。我对 React 的经验不够肯定,但我觉得这也与 React 的依赖系统混淆了。
另见React Hook Warnings for async function in useEffect
【参考方案1】:
在效果中创建一个异步函数,等待getData(1)
结果然后调用setData()
:
useEffect(() =>
const fetchData = async () =>
const data = await getData(1);
setData(data);
fetchData();
, []);
【讨论】:
为什么这与在 useEffect 挂钩之外定义异步函数有什么不同? @LelandReardon 如果你想在useEffect
钩子之外定义异步函数,你必须将它添加到 useEffect
的依赖列表中,并将其定义包装到 useCallback
中必要的依赖项以防止不必要的调用,有关更多信息,请查看反应文档here【参考方案2】:
如果您要立即调用它,您可能希望将其用作匿名函数:
useEffect(() =>
(async () =>
const data = await getData(1);
setData(data);
)();
, []);
【讨论】:
有什么好处? @Remi 该函数将被立即调用,不会在其他任何地方使用,因此不需要名称或任何预定义 如果组件被卸载,如何取消请求? @Remi 这与 OP 的问题无关,您可以在单独的线程上提出这个问题,因为它可能有不同的实现,其中大部分与您使用匿名异步还是预定义的无关用于获取数据 边缘案例。如果你没有取消它,那么 React 将设置一个状态,即使这个组件被卸载。认为一些测试库甚至会抱怨。因此,不推荐使用此示例。【参考方案3】:如果你按照警告的建议去做,那就最好了——在效果中调用异步函数。
function blabla()
const [data, setData] = useState(null);
useEffect(() =>
axios.get(`http://url/api/data/1`)
.then(result =>
setData(result.data);
)
.catch(console.error)
, []);
return (
<div>
this is the data["name"]
</div>
);
如果你想将 api 函数保留在组件之外,你也可以这样做:
async function getData(userId)
const data = await axios.get(`http://url/api/data/$userId`)
.then(promise =>
return promise.data;
)
.catch(e =>
console.error(e);
)
return data;
function blabla()
const [data, setData] = useState(null);
useEffect(() =>
(async () =>
const newData = await getData(1);
setData(newData);
)();
, []);
return (
<div>
this is the data["name"]
</div>
);
【讨论】:
感谢您的回答。不幸的是,这正是我不想做的。如果我的 URL 发生变化,我必须在我提取的所有文件上对其进行修补。为了可扩展性,我正在尝试做一些更像我发布的第一个代码的事情。【参考方案4】:由于 getData
返回一个 Promise,你可以使用 .then
useEffect(() =>
getData(1).then(setData);
, []);
【讨论】:
【参考方案5】:在解决 await 之前,组件可能会卸载或使用不同的 someId
重新渲染:
const unmountedRef = useRef(false);
useEffect(()=>()=>(unmountedRef.current = true), []);
useEffect(() =>
const effectStale = false; // Don't forget ; on the line before self-invoking functions
(async function()
// You can await here
const response = await MyAPI.getData(someId);
/* Component has been unmounted. Stop to avoid
"Warning: Can't perform a React state update on an unmounted component." */
if(unmountedRef.current) return;
/* Component has re-rendered with different someId value
Stop to avoid updating state with stale response */
if(effectStale) return;
// ... update component state
)();
return ()=>(effectStale = true);
, [someId]);
考虑将Suspense 用于需要在安装组件之前加载的数据。
【讨论】:
【参考方案6】:您仍然可以在钩子之外定义异步函数并在钩子内调用它。
const fetchData = async () =>
const data = await getData(1);
setData(data);
useEffect(() =>
fetchData();
, []);
【讨论】:
不推荐这样做,因为无法取消此异步函数中的setData
。
cancel
是什么意思?
@itwasmattgregg 你能举个取消的例子或进一步详细说明吗?除了最佳实践之外,我还没有看到任何关于为什么你不能这样做的明确答案。但是,最佳实践也可以将这些函数定义完全分解到另一个文件中。
@Dev 如果组件在 getData 正在运行时被卸载,那么 setData 会在事后尝试改变状态,react 会抛出一个警告它“表示内存泄漏”,它可能会或可能不会但是当组件不再存在时,它不应该做任何事情。这可以通过从 useEffect 返回一个设置标志的函数(react 在卸载时调用它)来避免,然后可以在调用 setData 之前检查该标志。以上是关于如何在 React 的 UseEffect() 中调用异步函数?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 useEffect 中使用 setTimeout 重置 React 钩子状态
如何在 React 中使用 useEffect 挂钩调用多个不同的 api
如何在 React 函数组件中不使用 useEffect 钩子获取数据?