异步流...使用 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,并确保所有状态仅在为didCancel
false 时才设置。
最后的话
这篇文章有很多内容需要解读。不过,我想先把这些内容从我的脑海中整理出来,并写在纸上。我知道其他人已经就这个主题写过更深入的文章,但希望这篇文章能够概括利用 useEffect 和异步的挑战部分。如有任何疑问,请随时在下方留言!
鏂囩珷鏉ユ簮锛�https://dev.to/silvestricodes/asynchronous-flows-with-react-hooks-1g0m