React-Query:解鎖你的應(yīng)用程序潛力,輕松解決接口請(qǐng)求難題!
2023-10-03 09:46 作者:塵緣如夢(mèng)_ | 我要投稿
- \n?}\n??\n這是一個(gè)組件拉取服務(wù)端數(shù)據(jù)的簡(jiǎn)單例子,在組件中,我們簡(jiǎn)單拉取了一個(gè)接口的數(shù)據(jù),并監(jiān)聽(tīng)接口的狀態(tài),根據(jù)狀態(tài)來(lái)更新不同的UI。\n如果有性能上的要求,那這里可能還需要加上一段緩存的邏輯\n?//...\n???try {\n?+???if(sessionUtil.get('users'){\n?+?????updateData(sessionUtil.get('users'));\n?+?????return;\n?+??}\n????const data = await axios.get('/api/user');\n?+??sessionUtil.set('users',data)\n????updateData(data);\n??} catch(e) {\n????setError(true);\n??}\n?//...\n至此,這個(gè)組件已經(jīng)變得相當(dāng)復(fù)雜了,如果組件拉取了好幾個(gè)接口,那么這一套邏輯還得寫(xiě)好幾遍。\n1 一些狀態(tài)管理庫(kù)的弊端"},{"attributes":{"header":2},"insert":"\n"},{"insert":"許多狀態(tài)管理庫(kù),比如redux,可以很流暢的管理頁(yè)面的狀態(tài),也有處理副作用的能力,但往往不能很好的處理服務(wù)端的狀態(tài),因?yàn)樘幚矸?wù)端的狀態(tài),通常還包括:\n緩存"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"將對(duì)同一數(shù)據(jù)的多個(gè)請(qǐng)求消除為一個(gè)請(qǐng)求"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"在后臺(tái)更新“過(guò)期”數(shù)據(jù)"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"知道數(shù)據(jù)何時(shí)“過(guò)期”"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"盡快反映數(shù)據(jù)更新"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"性能優(yōu)化,如分頁(yè)和延遲加載數(shù)據(jù)"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"管理內(nèi)存和服務(wù)器狀態(tài)的垃圾收集"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"使用結(jié)構(gòu)共享記憶查詢(xún)結(jié)果"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"直到React-Query的出現(xiàn),上面的問(wèn)題都變得迎刃而解。\n2 React-Query"},{"attributes":{"header":2},"insert":"\n"},{"insert":"React Query 是一個(gè)開(kāi)箱即用,零配置的服務(wù)端狀態(tài)管理庫(kù),支持Restful和GraphQL兩種類(lèi)型的請(qǐng)求,它能幫助你很好的獲取、同步、管理和緩存你的遠(yuǎn)程數(shù)據(jù)。它提供了幾個(gè)簡(jiǎn)單的Hooks,借助它們可以很輕松的完成對(duì)后端數(shù)據(jù)的增刪改查等操作,無(wú)需再寫(xiě)繁瑣的數(shù)據(jù)拉取和狀態(tài)判斷等代碼。\nReact-Query的官方文檔沒(méi)有大綱,閱讀起來(lái)相當(dāng)不方便,個(gè)人感覺(jué),直接閱讀github源碼項(xiàng)目中的docs要更方便一些。鏈接地址:"},{"attributes":{"bold":true},"insert":"https://github.com/TanStack/query/tree/main/docs/react"},{"attributes":{"blockquote":true},"insert":"\n"},{"insert":"2.1 簡(jiǎn)單使用\n2.1.1 QueryClientProvider\n首先,需要在組件外層定義一個(gè)queryClient作為組件操作和使用數(shù)據(jù)的一個(gè)共同容器,通過(guò)QueryClientProvider組件注入到項(xiàng)目中。\n?import {\n??QueryClient,\n??QueryClientProvider,\n??useQuery,\n?} from '@tanstack/react-query'\n??\n?const queryClient = new QueryClient()\n??\n?export default function App() {\n??return (\n???
\n????
\n? )\n?}\nuserQuery接收一個(gè)配置對(duì)象,其中\(zhòng)nqueryKey:必傳,用作請(qǐng)求數(shù)據(jù)緩存的唯一key值,也可以在數(shù)組中,寫(xiě)入多項(xiàng)如:['repoData', '1'],這樣React-Query在使用的時(shí)候會(huì)自動(dòng)把它拼接為/repoData/1,這個(gè)在緩存用戶(hù)訪問(wèn)過(guò)的頁(yè)面時(shí),非常有用。"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"queryFn:用于請(qǐng)求的方法,如果在QueryClient中配置了,這里可以不必再寫(xiě),需要返回請(qǐng)求完成后所處理的數(shù)據(jù)。"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"除了這兩項(xiàng)基本的參數(shù),useQuery還可以傳入上面defaultOptions的所有參數(shù),來(lái)表示對(duì)這個(gè)請(qǐng)求單獨(dú)的配置。\n然后useQuery會(huì)返回一個(gè)對(duì)象,里面包含著請(qǐng)求相關(guān)的所有信息,這些信息會(huì)隨著請(qǐng)求的進(jìn)度而改變,就無(wú)須我們?cè)偈褂靡唤Mstate變量來(lái)進(jìn)行管理了,常用的包括:\nisLoading:請(qǐng)求是否正在進(jìn)行"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"error:請(qǐng)求是否出錯(cuò),這里只會(huì)對(duì)500,404等做出反應(yīng),如果有其他情況錯(cuò)誤情況,需要在請(qǐng)求方法里面throw"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"isSuccess: 請(qǐng)求是否成功"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"status: 請(qǐng)求狀態(tài),包含上面幾種情況"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"data:返回?cái)?shù)據(jù)"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"除此之外,使用useQuery拉取回來(lái)的數(shù)據(jù),會(huì)被默認(rèn)緩存起來(lái),然后可以通過(guò)配置過(guò)期時(shí)間,重新拉取等策略來(lái)進(jìn)行管理。\n2.1.3 useQueryClient\n通過(guò)useQueryClient,我們可以獲取到之前注入的容器實(shí)例,里面保存著所有我們緩存的信息,以及配置信息,而它本質(zhì)上其實(shí)也是對(duì)React.useContext的封裝。\n以上面Example組件為例,如果我們想在另一個(gè)組件訪問(wèn)這些數(shù)據(jù)。\n?function Example() {\n??const queryClient = useQueryClient()\n??\n??const data = queryClient.getQueryData(['repoData'])\n??\n??return (\n???//...\n? )\n?}\n除了訪問(wèn)緩存數(shù)據(jù),queryClient還有很多API:\n"},{"attributes":{"bold":true},"insert":"queryClient.fetchQuery"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.fetchInfiniteQuery"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.prefetchQuery"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.prefetchInfiniteQuery"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.getQueryData"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.refetchQueries"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.cancelQueries"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.removeQueries"},{"attributes":{"list":"bullet"},"insert":"\n"},{"attributes":{"bold":true},"insert":"queryClient.resetQueries"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"..."},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"大家可以根據(jù)需要去官網(wǎng)查閱。\n2.1.4 useMutation\n除了獲取數(shù)據(jù),很多時(shí)候還需要處理數(shù)據(jù)的修改,比如說(shuō)最簡(jiǎn)單的todo list例子,除了拉取數(shù)據(jù)列表,還需要增刪改數(shù)據(jù),而這個(gè)時(shí)候除了需要發(fā)送接口,還需要修改本地的數(shù)據(jù),React-Query提供了useMutation來(lái)幫我們完成這些事情。\n以上面Example組件為例,我們已經(jīng)拉取到了data,現(xiàn)在我們想新增一條數(shù)據(jù),那我們可以\n?const {isLoading,isError,isSuccess,mutate} = useMutation({\n???mutationFn: async (newData) => insertNewData(newData),\n???onSuccess: () => {\n????queryClient.invalidateQueries({ queryKey: ['repoData'] });\n??},\n???onError: (error) => {\n????console.log(error)\n??}\n? });\n這里我們傳入了:\nmutationFn:代表元數(shù)據(jù)的方法"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"onSuccess:接口調(diào)用成功后的回調(diào)"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"onError: 失敗的回調(diào)"},{"attributes":{"list":"bullet"},"insert":"\n"},{"insert":"返回的數(shù)據(jù)和useQuery基本是相同的,這里的mutate則是觸發(fā)更改的方法,如果我們想執(zhí)行useMutation中傳入的方法,我們只需要調(diào)用mutate即可,傳給mutate的參數(shù)都會(huì)被帶到useMutation的構(gòu)造方法中。\n??const updateData = async (newData) => {\n???mutate(newData);\n? };\n\n\n以上就是React-Query最核心的對(duì)服務(wù)端數(shù)據(jù)進(jìn)行增刪改查的功能,除此之外,React-Query還有很多其他的能力。\n2.2 數(shù)據(jù)預(yù)獲取\n有時(shí)候我們不需要整個(gè)頁(yè)面loading來(lái)等待數(shù)據(jù)加載,我們更希望在用戶(hù)操作之前就拉取完數(shù)據(jù),比如用戶(hù)hover詳情鏈接,而不是點(diǎn)擊詳情的時(shí)候。\n那我們可以使用queryClient的prefetchQuery方法,提前拉取到用戶(hù)可能會(huì)訪問(wèn)的數(shù)據(jù),并加入到緩存中,由于不需要監(jiān)聽(tīng)服務(wù)端狀態(tài)等,所以這個(gè)方法會(huì)比useQuery高效許多。\nonMouseEnter={async () => {\n await queryClient.prefetchQuery({\n queryKey: ['character', char.id],\n queryFn: () => getCharacter(char.id),\n staleTime: 10 * 1000, // only prefetch if older than 10 seconds\n })\n}}\n2.3 分頁(yè)緩存\n通過(guò)動(dòng)態(tài)設(shè)置queryKey,并將keepPreviousData設(shè)置為true,我們可以很輕松的緩存之前頁(yè)碼的數(shù)據(jù)\n const { status, data, error, isFetching, isPreviousData } = useQuery({\n queryKey: ['projects', page],\n queryFn: () => fetchProjects(page),\n keepPreviousData: true,\n staleTime: 5000,\n })\n2.4 滾動(dòng)列表渲染\n使用useInfiniteQuery定義拉取數(shù)據(jù)的方法,以及上下頁(yè)的邏輯,然后會(huì)返回更新頁(yè)面數(shù)據(jù)的狀態(tài),以及觸發(fā)更新的方法。\n const {\n status,\n data,\n error,\n isFetching,\n isFetchingNextPage,\n isFetchingPreviousPage,\n fetchNextPage,\n fetchPreviousPage,\n hasNextPage,\n hasPreviousPage,\n } = useInfiniteQuery(\n ['projects'],\n async ({ pageParam = 0 }) => {\n const res = await axios.get('/api/projects?cursor=' + pageParam)\n return res.data\n },\n {\n getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,\n getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,\n },\n )\n2.5 devTools配套開(kāi)發(fā)工具\(yùn)n導(dǎo)入開(kāi)發(fā)工具\(yùn)nimport { ReactQueryDevtools } from 'react-query/devtools'\n\n{data.name}
\n????{data.description}
\n?????? {data.subscribers_count}{' '}\n????? {data.stargazers_count}{' '}\n?????? {data.forks_count}\n???標(biāo)簽: