使用react-query的自定义hook简化数据获取。
来源:公众号《前端全栈开发者》
在使用React构建应用程序时,我们面临的挑战之一是确定用于从服务器获取数据的代码模式。在React中处理数据获取的最常见方法是使用全局状态作为一种机制来确定获取操作的当前状态。
以下是从《星球大战》 API获取数据的示例:
import React, {useState, useEffect} from 'react';import axios from 'axios';// 使用axios定期获取function App() { const [isLoading, setLoading] = useState(false) const [isError, setError] = useState(false) const [data, setData] = useState({}); useEffect(() => { const fetchData = async () => { setError(false); setLoading(true); try { const response = await axios('http://swapi.dev/api/people/1/'); setData(response.data); } catch (error) { setError(true); } setLoading(false); }; fetchData() }, []); return (
使用《星球大战》 API的React Query示例
{isError &&
出了些问题 ...
} {isLoading ? (
Loading ...
) : (
{JSON.stringify(data, null, 2)}
)}
);}export default App;
上面的代码需要 useState 和 useEffect 两个钩子,并使用三个不同的状态来存储数据,并判断应用程序是在获取数据还是已经有了API的错误响应。对于大多数应用程序数据获取逻辑,将反复重复此模式。
不仅如此。 React中数据获取的最常见问题包括:
- 数据在所有应用程序实例之间共享,并且可以由其他人更改
- 数据可能是“过时的”,需要刷新
- 处理缓存和使数据无效以优化请求操作
最后,还有一个问题是,本地状态通常存储用户的偏好,如主题和侧栏配置,而远程状态则存储从API获取的数据:
//现在的全局状态通常是什么样子const state = { theme: "light", sidebar: "off", followers: [], following: [], userProfile: {}, messages:[], todos:[],}
如果你能把本地状态和远程状态分开,那不是很好吗?而如果你能减少数据获取所需编写的模板代码量呢?
我们将在这里深入探讨的另一种解决方案是React Query。这个库将帮助你获取、同步、更新和缓存你的远程数据,同时通过为你提供两个简单的hooks和一个实用函数来减少你需要编写的代码量。
要想在这篇文章中得到最大的收获,请下载我从作者那里修改的这个repo示例。
这个小型的React应用程序将使用axios从API路由中检索字符串数组。你可以通过使用提供的表单将一个新的字符串放入数组中。它还打开了React Query DevTools,所以你可以实时看到缓存的数据。
React Query DevTools:https://github.com/tannerlinsley/react-query-devtools
useQuery hook
useQuery hook是用于将数据获取代码注册到React Query库中的功能。它采用一个任意键和一个异步函数来获取数据,并返回各种值,你可以用这些值来告知用户当前的应用状态。
例如,让我们重构上面的《星球大战》 API示例:
import React from 'react';import axios from 'axios';import {useQuery} from 'react-query';// 使用axios获取react-queryfunction App() { const { isLoading, error, data } = useQuery('fetchLuke', () => axios('http://swapi.dev/api/people/1/'))return (
R使用《星球大战》 API的React Query示例
{error &&
出了些问题 ...
} {isLoading ? (
检索Luke Skywalker信息...
) : (
{JSON.stringify(data, null, 2)}
)}
);}export default App;
请注意,在这段代码中,我们没有使用常规的 useState 和 useEffect Hook。这是因为 useQuery 已经具有可以在应用程序内部使用的各种值,例如 isLoading、error 响应和返回的 data。
回到示例repo,你可以看到我是如何通过在 /pages/index.js 中使用相同的 useQuery 函数从api中获取todo列表的。
const { status, data, error, isFetching } = useQuery('todos', async () => { const { data } = await axios.get('/api/data') return data})
这里的不同之处在于,我还使用了 isFetching 来确定查询当前是否刷新数据。稍后你将了解其重要性。现在,让我们学习如何修改远程数据。
useMutation hook
useMutation hook通常用于创建/更新/删除远程数据。这个函数接收一个异步函数来更新你的数据(通常是在 POST、PUT 或 DELETE 请求之间),并返回一个你可以调用的mutate函数来触发突变。
const [mutate] = useMutation( text => axios.post('/api/data', { text }),)mutate("学习有关React Query知识")
你还可以放置仅在mutate函数返回某些结果(例如 onSuccess 和 onError)时才触发的可选函数。在示例repo中,你可以看到,当表单提交时,我使用mutate函数将新数据发布到API中。发布请求成功后,我还将文本输入重新设置为空:
const [mutatePostTodo] = useMutation( text => axios.post('/api/data', { text }), { onSuccess: () => { // 查询无效 // queryCache.invalidateQueries('todos') setText('') }, })
但是,如果你尝试在演示应用程序中插入新文本,则会看到待办事项列表没有刷新。为了告诉React Query重新获取待办事项列表,你需要取消注释 setText 函数上方的代码:
{ onSuccess: () => { // 查询无效 queryCache.invalidateQueries('todos') setText('') },}
queryCache.invalidateQueries 将使带有 todo 键的缓存无效,并使React Query重新获取数据。
queryCache实用程序
正如你在演示DevTools中看到的,React Query将把检索到的数据缓存在上面的 todo 键下,但是在获取后它会自动失效,除非你配置了 staleTime。
queryCache 是一个实用程序实例,其中包含许多可用于进一步处理查询的功能。在示例中,你已经了解了如何使用 queryCache.invalidateQueries 函数使React Query发送新请求以获取待办事项列表。
总结
React Query是一个很好的钩子库,用于管理数据请求,它完全消除了把你的远程数据放在全局状态里面的需要。你只需要告诉库你需要在哪里获取数据,它就会处理缓存、后台更新和陈旧的数据,无需任何额外的代码或配置。
React Query还消除了对 useState 和 useEffect Hook的需要,并用几行React Query逻辑替换了它们。从长远来看,它绝对可以帮助你保持应用程序的可维护性,响应性和快速性。
如果你想了解更多信息,请不要忘记查看React Query文档。