为什么useEffect不适合进行API调用 译文
作者丨Rojan Maharjan
译者 | 涂承烨
React团队在useEffect钩子中做出的设计选择仍然是一个热议的话题。有些人喜欢,有些人不喜欢。
如果你不是来自React世界,这听起来肯定很奇怪,因为它的默认行为是非常容易遇到的可怕的“无限渲染循环”。例如:
useEffect(()=>{console.log("Hello World")})
? ? ? ?看起来很好,对吧?
不,这将在每次渲染中打印“Hello World”,React dev模式下强迫症的我会迫不及待地将结果放置到第二个参数的依赖数组中,像这样:
useEffect(()=>{console.log("Hello World")},[])
因此,我们必须始终手动确保每次使用它时不会陷入无限循环。
就这样,我们修复了这个问题,上面的代码应该只运行一次,对吗?
技术上来说,但并不总是如此……
严格模式问题
在开发模式下,如果你想享受React严格模式的好处,这实际上会被调用两次。你已经知道为什么这是一个问题了。以下面的代码为例:
useEffect(()=>{
api.post("/view",{})
},[])
假设我们有一个代码,它发送一个事件,说用户像上面的useEffect一样查看了页面。在严格模式下,React将运行这个效果两次,并发送一个双重事件。
你可以让hacky参考下面这样来工作:
export default function useEffectOnce(fn: () => void) {
const ref = useRef(false);
useEffect(() => {
if (ref.current) {
fn();
}
return () => {
ref.current = true;
};
}, [fn]);
}
但从本质上说,这是一个问题,至少不是一个优雅的解决方案。
性能问题
根据官方文档,useEffect钩子在整个UI或组件渲染完成后运行。因此,当我们在其中放入一个API调用时,API调用将在UI完成完整呈现后启动,如下所示:
这并不是最优的,因为尽管react是快速的,但渲染UI总是会消耗一些时间,它将延迟我们的API调用,而这也可以在渲染开始时运行。
因此,更好的方法是获取数据且并行渲染它。
我们该怎么做呢?
React查询来解决
React query和我们讨论的完全一样,像useQuery这样的钩子会在渲染开始时立即获取数据,所以你不必等待React加载完整个组件,如下所示:
下面是一个例子:
// with react query
const { status, data, error, isFetching } = useQuery(
['data'],
async () => {
const data = await (
await fetch(`${API_BASE_URL}/data`)
).json()
return data
}
)// without react query
useEffect(() => {
try {
setLoading(true)(async () => {
const data = await (await fetch(`${API_BASE_URL}/data`)).json();
setData(data);
})();
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}, []);
通过查看语法,我们可以看到react query不仅在页面加载时立即执行query,而且还在react query返回的单个对象中处理许多事情,如加载状态、错误状态和实际数据。
此外,重新运行/使查询无效很简单,如下所示。
queryClient.invalidateQueries(['data'])
其他一些著名的库也解决了这些问题,如SWR、URQL和Apollo Client。
解决该问题的另一种方法是执行SSR,以便数据先在后端渲染,或者使用react-router 加载器等功能。
结论
对useEffects进行API调用可能很容易出错或非常慢。所以,除非不得已,否则最好避免使用。同时,建议你通过一些库来处理数据的获取更为合适。
译者简介
涂承烨,51CTO社区编辑,信息系统项目管理师、信息系统监理师、PMP,某省综合性评标专家,拥有15年的开发经验。对项目管理、前后端开发、微服务、架构设计、物联网、大数据等较为关注。
原文链接:??https://articles.wesionary.team/why-useeffect-is-a-bad-place-to-make-api-calls-98a606735c1c??