异步流...使用 React Hooks!

2025-06-10

异步流...使用 React Hooks!

前几天我在工作中有机会实现一些异步数据流,我很乐意与大家分享我的方法。

思维过程

每当我处理异步数据的加载和显示时,我倾向于将数据加载工作和数据显示工作分成两个部分。对我来说,这种关注点分离有助于我专注于清晰易懂的逻辑树。

设置我们的加载器

以下是我们希望加载组件处理的内容:

  • 当组件安装时,我们应该触发我们的 api 调用来获取我们的数据。
  • 当这个api调用触发时,我们应该设置某种加载状态。
  • 当 api 调用完成时,我们应该将数据设置为状态并表明我们的加载已完成。
  • 我们应该将这些数据传递给其他组件。

根据该列表,我们需要两种状态——加载和数据。我们还需要弄清楚如何将状态挂载到组件的挂载中。让我们首先使用useState 钩子来设置状态。

  import React, { useState } from 'React'
  import Breakfast from './Breakfast' // I utilize breakfast foods as my foo/bar/biz/baz

  const DataLoader = () => {
    const [ isLoading, setIsLoading ] = useState(false)
    const [ data, setData ] = useState([])

    return isLoading ? <div>Loading</div> : <Breakfast data={data} />
  }

好了,状态设置好了!现在我们需要进行 API 调用。为了更容易理解,我会把它拆分成一个新的部分。

useEffect

useEffect是我们处理挂载和更新的方式。此函数允许我们捕获函数组件中的副作用以供使用。其文档的概要如下:

  useEffect(callback, dependencyArray)

useEffect 可以通过两种方式触发:组件挂载时,以及 dependencyArray 中某个值发生变化时。如果传递一个空数组作为第二个参数,则可确保 useEffect 仅在组件挂载时运行。

我们将在 useEffect 中使用一个异步函数。需要注意的是,我们不能将回调函数设置为异步函数,因为 useEffect 必须返回一个清理函数或不返回任何内容。稍后您会看到我使用async/await方法来声明 Promise。async 函数会隐式地返回一个 Promise,因此,如果没有时间点,即使现在是一个 Promise 化的 useEffect,它也会崩溃!但是,在 useEffect 中使用 async 函数完全没问题。

-  import React, { useState } from 'React'
+  import React, { useState, useEffect } from 'React'
   import Breakfast from './Breakfast'

  const DataLoader = () => {
    const [ isLoading, setIsLoading ] = useState(false)
    const [ data, setData ] = useState([])

+   useEffect(() => {
+     async function fetchData() {
+       setIsLoading(true)
+       const fetcher = await window.fetch(/some/endpoint)
+       const response = await fetcher.json()
+       setData(response)
+       setIsLoading(false)     
+     }
+     fetchData()
    }, [])


    return isLoading ? <div>Loading</div> : <Breakfast data={data} />
  }

上述函数的工作原理如下:

  • 如果依赖数组为空,则此 useEffect 将仅在挂载时运行。
  • 当组件挂载时,运行fetchData。
  • 触发我们的加载状态。利用Fetch API(我们做到了!)来解析一个 Promise 并获取响应。
  • .json使用函数解析响应来解决该承诺。
  • 将我们的数据状态设置为此响应,并将我们的加载状态设置为 false。

在每个状态改变时,我们都会使用适当的 UI 重新渲染。

这就是我们的加载器!就 React 组件而言,接收数据的组件非常标准,所以我就不讨论示例的这部分了。

改进

错误处理

我们还可以对 useEffect 的设置做更多的事情。我们先来谈谈错误处理。

Async/Await它非常适合try/catch/finally块,所以让我们尝试一下。让我们提取 useEffect 的内部部分,并将 try/catch/finally 添加到其中。

     async function fetchData() {
       setIsLoading(true)
+      try {
        const fetcher = await window.fetch(/some/endpoint)
        const response = await fetcher.json()
        setData(response)
+      } catch (error) {
+        // Do something with error
+      } finally {
+        setIsLoading(false)   
+      }  
     }
     fetchData()

try部分代码会尝试调用 API。如果发生任何错误,就会进入 catch 语句。当这两项操作都完成后,无论结果如何,我们都会触发 finally 代码块并清除加载状态。

清理

处理组件卸载的情况是个好主意,这样我们就不用继续设置状态了。useEffect支持在组件卸载时运行的清理函数。让我们添加这个功能。

    useEffect(() => {
+    let didCancel = false
     async function fetchData() {
+      !didCancel && setIsLoading(true)
       try {
        const fetcher = await window.fetch(/some/endpoint)
        const response = await fetcher.json()
+       !didCancel && setData(response)
       } catch (error) {
           // Do something with error
       } finally {
+       !didCancel && setIsLoading(false)   
       }  
     }
     fetchData()
+    return () => { didCancel = true }
    }, [])

我们添加的返回函数将在组件卸载时运行。这会将 didCancel 设置为 true,并确保所有状态仅在为didCancelfalse 时才设置。

最后的话

这篇文章有很多内容需要解读。不过,我想先把这些内容从我的脑海中整理出来,并写在纸上。我知道其他人已经就这个主题写过更深入的文章,但希望这篇文章能够概括利用 useEffect 和异步的挑战部分。如有任何疑问,请随时在下方留言!

鏂囩珷鏉ユ簮锛�https://dev.to/silvestricodes/asynchronous-flows-with-react-hooks-1g0m
PREV
如何使用 React Hola Mundo 创建滚动到顶部按钮!
NEXT
如何用 ExpressJS 最简单地创建 TypeScript 项目!作者:SilvenLEAF