为什么不要在 useEffect 中进行 API 调用?

简介: 为什么不要在 useEffect 中进行 API 调用?

随着 React 的版本更新,内置 Hooks 越来越多了。很多 Hook 可能你压根都没听说过。但是 useEffect 这个老牌 Hook,相信每个用 React 的同学应该熟悉。


不优雅的 useEffect


不过对很多刚接触 React 的人来说,使用 useEffect 非常容易出现无限渲染循环的问题。

比如这样写:


const [count, setCount] = useState(0)
useEffect(() => {
  setCount(count+1)
})

原因是,useEffect 如果不传递第二个参数,它就会在组件每次渲染后执行。而 setCount 将会导致组件渲染。所以就会出现无限渲染循环的问题。

为了防止这个问题,我们可以给它设置第二个依赖项。依赖项是一个数组,可以传递多个引用,当其中的任意一个引用发生变化时,都会触发 effect。


const [count, setCount] = useState(0)
useEffect(() => {
  setCount(count+1)
}, [])

所以我们通常都需要设置第二个依赖项来防止意想不到的事情发生。

useEffect 的设计被很多人诟病,当然也有人很喜欢它。但我认为 useEffect 在设计上确实有些草率了。


严格模式怎么办?


useEffect 可以做很多事,最常见的事情就是发起 API 调用。像下面这样:


useEffect(() => {
  fetch('/xxx')
}, [])

React 中有严格模式,如果开启了严格模式,那么 useEffect 将会被触发两次。

这么做的好处是提醒我们,这个组件是具有副作用的。

但是对应的,我们会发起两次 API 请求。这显然是不合适的。

我知道很多人不喜欢 React 严格模式,他们会选择直接关闭它。但是如果我就是想要严格模式带来的其他好处,该怎么办?

其实也很简单,我们只需要用一个在组件每次重新渲染时不会影响的东西来记录是否被调用过就可以了。很容易就可以想到 useRef 这个东西。

所以我们可以自己实现一个在严格模式下保证 useEffect 只运行一次的 Hook。


function useEffectOnce(fn: () => void) {
  const canCall = useRef(false)
  useEffect(() => {
    if(canCall.current) {
      fn()
    }
    return () => {
      canCall.current = true
    }
  }, [])
}

这样就解决这个问题,但是它并不优雅。


性能并不好


如果在 useEffect 中发起 API 调用,那么 API 的调用将会在组件渲染完成才开始。

整个流程如下:

image.png

这个流程肯定不是最好的,因为渲染 UI 总归是要花费一些事件,尽管可能很短暂,你或许认为它无关紧要。但是性能问题往往就是积少成多导致的。


我们到底该怎么办?


对于这个问题,TanStack Query 提供了 useQuery Hook。

利用它,我们可以在组件开始渲染时同时开始 API 调用。

流程就像下面这样:

image.png

下面是一个使用 useEffect 的代码示例。


useEffect(() => {
  try {
    setLoading(true)(async () => {
      const data = await (await fetch('/data')).json();
      setData(data);
    })();
  } catch (error) {
    setError(error);
  } finally {
    setLoading(false);
  }
}, []);

可以看到,我们除了需要处理数据以外,还要处理加载状态和异常状态。

如果使用 useQuery 会怎么样呢?


const { status, data, error, isFetching } = useQuery(
  ['data'],
  async () => {
    const data = await (
      await fetch('/data')
    ).json()
    return data
  }
)

它会帮我们处理好加载状态、错误状态和查询结果数据的更新。

如果我们想重新运行或者终止这个 API 调用也非常简单。

只需要调用 useQuery 对应的 API 就可以了。


queryClient.invalidateQueries(['data'])

除了 useQuery,我们还可以使用 SWR,它也是一个非常棒的库。

关于 SWR,我还有一篇单独介绍它的文章。

React 数据请求最佳实践


总结


在 useEffect 中进行 API 调用很容易出错,并且性能也不好。最好的方式就是放弃这种用法,直接采用类似 useQuery 或者 SWR 这类库,可以让我们更好的进行 API 调用。



相关文章
|
7月前
|
JavaScript API
vue选项式API和组合式Api
vue选项式API和组合式Api
|
JavaScript API
MutationObserver API
MutationObserver API
74 0
|
26天前
|
JavaScript 前端开发 API
探索组合式API与Options API的对比及最佳实践
探索组合式API与Options API的对比及最佳实践
|
4月前
|
JavaScript 安全 API
Vue 3 Composition API 与 Options API:全面比较两者的区别和优缺点
Vue 3 Composition API 与 Options API:全面比较两者的区别和优缺点
|
6月前
|
前端开发 API
|
7月前
|
JavaScript 前端开发 API
组合API:掌握Vue的组合式API(Composition API)
【4月更文挑战第24天】Vue.js的组合式API是Vue 3中的新特性,旨在提供更灵活的组件逻辑组织方式。它允许开发者像React Hooks一样定义和复用逻辑单元——组合函数。通过组合函数,可以跨组件共享和管理状态,提升代码复用和维护性。本文介绍了如何开始使用组合式API,包括安装Vue CLI、引入API、使用组合函数以及组织和复用逻辑。掌握组合式API能增强开发复杂应用的能力,改善代码结构和可读性。
107 2
|
前端开发 JavaScript API
Promise API 中的静态方法
捋一捋 Promise 魔法书里的各种静态方法,看看它们有什么用,怎么用,还能给我们带来什么惊喜!🎉
89 1
|
7月前
|
JavaScript API
Vue3 API函数及功能
Vue3 API函数及功能
40 0
|
7月前
|
JavaScript API
Composition Api 与 Options Api 有什么区别?
Composition Api 与 Options Api 有什么区别?
139 0
|
小程序 前端开发 API
小程序api封装 promise使用
小程序api封装 promise使用
68 0