react-query从拒绝到拥抱
react-query
是一位数据获取专家,能够智能管理请求的一切内容,包括数据、状态、缓存,更新等,基于Hooks。
其次他并不限定你使用发起请求的库,所以你可以使用任何你想使用的请求方式,再次强调,他是一个管理高手,他把数据获取从混乱变成秩序,从复杂变成简单,从讨厌变成喜欢。
当我第一次开始使用的时候,就对他有了偏见,难学!上手并不是很友好,不符合我过去获取数据的直觉和经验,但奇怪的是却极受开发者欢迎。由于过去的经验和靠表面的直觉差点就让我错过了如此棒的库。
好吧,现在让我来带你一步步卸下他复杂的面具,以及他是如何改变了数据请求的方式。
首先,先来看看过去我们是如何获取请求数据的:
import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [starCount, setstarCount] = useState(0); useEffect(() => { //获取react-query的star数量 axios .get("https://api.github.com/repos/tannerlinsley/react-query") .then((res) => { setstarCount(res.data.stargazers_count); }) .catch((err) => { //处理错误 console.log(err); }); }, []); return <div>react-query获得了{starCount}颗星</div>; } 复制代码
那么现在需要加个需求,由于网络可能较慢,需要加个loading和err,来解决用户等待响应过程的难受和出错后让用户可以点击按钮重新获取数据,好那就加上吧。
import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [starCount, setstarCount] = useState(0); const [loading, setloading] = useState(false); //声明loading状态 const [isErr, setisErr] = useState(false); //声明错误状态 useEffect(() => { setloading(true); //开始请求数据,loading设为true axios .get("https://api.github.com/repos/tannerlinsley/react-query") .then((res) => { setloading(false); //请求结束,loading设为false setstarCount(res.data.stargazers_count); }) .catch((err) => { //处理错误 setloading(false); setisErr(true); //设置isErr为true console.log(err); }); }, [isErr]); const handleReload = () => { setisErr(false); //重置isErr为false,再次发送请求 }; if (loading) return <span>数据获取中...</span>; if (isErr) return <button onClick={handleReload}>重新获取数据</button>; return <div>react-query获得了{starCount}颗星</div>; } 复制代码
看到以上新增的管理loading和err状态的代码,你的负担是否大了很多?为了进一步增强应用和体验,比如网络错误自动重试,为了防止用户看到的是旧的数据,你需要增加窗口焦点时重新自动获取数据等,可以看出如此发展下去,组件需要管理的状态越来越多,你也会越来越力不从心,状态的增多,导致你的组件更容易出bug,很大可能会造成你忘记去修改或重置它们的状态,因为这些状态分布零散,同时这也会造成将来的代码是多么难以维护和扩展,这会是一场噩梦。
下面来看看react-query是如何把这件事变成乐趣的。
import axios from "axios"; import { useQuery } from "react-query"; export default function App() { const { data, isLoading, error } = useQuery("getStar", () => axios.get("https://api.github.com/repos/tannerlinsley/react-query") ); if (isLoading) return "数据获取中..."; if (error) return "发生错误: " + error.message; return ( <div>react-query获得了{data.stargazers_count}颗星</div> ); } 复制代码
在这里使用useQuery
,此刻这个请求拥有了自动获取数据,管理请求状态,错误重试,窗口焦点自动获取数据,缓存等,它的第1个参数是一个唯一的key,名字有意义就好,第2个参数是请求数据的方法,返回Promise,它还有第3个参数是个配置选项的对象(后面会说)。最后它会返回一个结果,结果里面包含请求的数据,加载状态,错误等,这样这个请求就把所有这些状态串联起来,而不是一堆散乱的状态,突然逻辑变得清晰了,你只需要根据这些状态处理页面,一切都简单了。
react-query
三大核心概念
Queries
useQuery
:发起单个请求useQueries
:发起多个请求useInfiniteQuery
:用于无限加载的列表,非常强大,让构建无限加载组件变得简单。
Mutations
useMutation
:用来创建、更新、删除数据,当你的接口涉及这些逻辑时你可以使用它。
Query Invalidation
你所用的query
有时需要刷新以重新获取最新数据,这时候你就可以用QueryClient
的来使某个query
失效,然后该query
就会重新去获取数据。QueryClient
非常强大,它也可以对query
进行全局配置,操作缓存,移除或重置query
等等
重点
我在项目中,一般只需要用到Queries
和Mutations
,这两个足以满足大部分网络请求的需求,所以我在这里主要是讲这两个。
下面来看下Queries
的配置对象
Queries options
配置对象就是第3个参数,它是一个对象,这个配置对象在useQueries
,useInfiniteQuery
中也相同,这个对象有数十个参数可供配置,这里我只挑其中几个在代码中注释说明。
const returns = useQuery(queryKey, queryFn?,{ enabled, //默认为true,表示自动请求,false的话则需要你手动 retry, //请求失败后,请求的重试次数,也可以为boolean,true为无数次重试,false则不会重试 refetchOnWindowFocus,//页面取得焦点时,重新获取数据,默认为true staleTime, //指定缓存时长,以毫秒为单位。 ... }) 复制代码
Queries Returns
下面来看看它的返回的对象,只挑其中几个说明:
const { data, //这个就是请求成功回来的数据 isLoading, //true表示数据在获取的路上 error,//错误对象,如果存在则包含相关的错误信息 refetch,//这个还挺实用的,你可以在需要的地方或需要更新数据时调用,则会触发这个请求,比如enabled=false时 ... } = useQuery(queryKey, queryFn?,options?) 复制代码
Mutation(突变)
用来创建/更新/删除数据时使用,最典型的例子就是一个todoList
,对todo
进行增删改相关的请求。使用useMutation
hooks。
//例子来自官网 function App() { //创建一条todo的mutation请求 const mutation = useMutation(newTodo => { return axios.post('/todos', newTodo) }) return ( <div> {mutation.isLoading ? ( 'Adding todo...' ) : ( <> {mutation.isError ? ( <div>An error occurred: {mutation.error.message}</div> ) : null} {mutation.isSuccess ? <div>Todo added!</div> : null} <button onClick={() => { //主要看这里,mutate方法传递请求的参数,来创建一条新的todo mutation.mutate({ id: new Date(), title: 'Do Laundry' }) }} > Create Todo </button> </> )} </div> ) } 复制代码
也是挺简单的,记住增删改就用它对了,获取的话就用useQuery
。
扩展(选看)
QueryClient、QueryClientProvider、useQueryClient 这三个可以用来进行query的全局配置、与缓存交互等
//例子来自官网,有一定的修改。 import React from "react"; import ReactDOM from "react-dom"; import { QueryClient, QueryClientProvider, useQuery } from "react-query"; import { useQueryClient } from 'react-query'; import { ReactQueryDevtools } from "react-query/devtools"; //调试工具 const queryClient = new QueryClient();//创建实例,可以用该实例配置一些选项,具体看文档 export default function App() { return ( //注入到Example里 <QueryClientProvider client={queryClient}> <Example /> </QueryClientProvider> ); } function Example() { const queryClient = useQueryClient() //获取QueryClient实例 const { isLoading, error, data, isFetching } = useQuery("repoData", () => fetch( "https://api.github.com/repos/tannerlinsley/react-query" ).then((res) => res.json()) ); if (isLoading) return "Loading..."; if (error) return "An error has occurred: " + error.message; return ( <div> <h1>{data.name}</h1> <p>{data.description}</p> <strong>???? {data.subscribers_count}</strong>{" "} <strong>✨ {data.stargazers_count}</strong>{" "} <strong>???? {data.forks_count}</strong> <div>{isFetching ? "Updating..." : ""}</div> {/* 指定key为repoData,可以该query重新获取数据 */} <button onClick={()=>queryClient.refetchQueries('repoData')}>重新获取</button> <ReactQueryDevtools initialIsOpen /> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 复制代码
QueryClient
的api非常多,感兴趣的你可以去深入探索。
总结
如果你是用hooks开发组件的话,非常建议你使用,它会让你的开发之旅更自在。这个库非常强大,不可能一一解释,你可以根据自己的需要,自己去琢磨。就到这里了,文章中有任何错误,以及有你不理解或困惑的地方,欢迎留言。
作者:前端钢铁侠
链接:https://juejin.cn/post/7038950225540546596
伪原创工具 SEO网站优化 https://www.237it.com/