扣丁書屋

使用React Query做為axios請求庫的上層封裝

前言

在項目中,通常都需要跟服務端進行異步的數據交互,基本都是用到axios這個庫來做請求,嗯,畢竟擁有80k star,明星項目

接下來,我們來回顧下axios在項目中的使用

以查詢用戶信息為例,我們會這樣封裝

async function requestUsers(){
  const {data} =await axios.get('/api/users');
  return data;
}

我們再用hooks再封裝下這個請求,包括loading等中間態的封裝,處理的優雅一點

import React, {useState,useEffect} from 'react';
import axios from 'axios';
function useUsersQuery(){
  const [data,setData] = useState([]);
  const [isLoading,setLoading] = useState(false);
  const [isError,setError] = useState(false)
  useEffect(()=>{
    (async()=>{
       setLoading(true);
       try{
          const {data} = await axios.get('/api/users');
          setData(data);
       } catch((()=>{
          setError(true);
       })
       setLoading(false);
    })()
  })
  return { 
     data,
     isLoading,
     isError
  };
}
function UserList(){
  const {data, isLoading,isError} = useUsersQuery();
  if (isLoading) {
        return <div>loading</div>;
   }
  if (isError) {
        return <div>error</div>;
   }
  return (
    <div>
       {
         data.map((item)=>{
            return <div>{item.name}</div>
         })
       }
    </div>
  )
}

可以看到,我們的項目中基本上是這樣封裝請求,我們不僅要請求數據,還要處理相應的loading,error這些中間態,這類通用的中間狀態處理邏輯可能在不同組件中重復寫很多次。

另外,現在的前端項目特別是單頁面應用,會使用Flux、Redux、Mobox等狀態管理庫,會把組件間共享的數據都存放在狀態管理庫中,這些可以分為兩類,一類是用戶交互的中間狀態,比如isLoading,isClose,modalVisible等等,另外一類就是服務端狀態(數據)

我們一般處理的方式都是無差別的存放在全局狀態管理上,狀態管理庫為了兼容異步請求,就有了redux-saga,redux-action這些異步解決方案

其實對于redux等狀態管理庫,本身是沒有異步這個概念,只有mutation這種操作,為了支持異步,硬是強加了異步action這種操作,實際這些異步中間件就是在最后的請求回調透傳了dispatch,諸如這些情況,我們不僅將數據一鍋燉放在全局狀態管理上,寫法上也使得項目越來越臃腫了(以至于出現后面rematch、dva方案進行簡化),我們有沒有想過,服務端的狀態就不應該放在全局狀態管理上,全局狀態管理應該專門處理用戶交互的中間狀態

接下來,就是引出今天的主角 React Query

React Query

React Query 通常被描述為 React 缺少的數據獲取(data-fetching)庫,但是從更廣泛的角度來看,它使 React 程序中的獲取,緩存,同步和更新服務器狀態變得輕而易舉。

解決了什么問題

服務端狀態有以下特點:

  1. 存儲在遠端,本地無法直接控制
  2. 需要異步 API 來查詢和更新
  3. 可能在不知情的情況下,被另一個請求方更改了數據,導致數據不同步

現有的狀態管理庫(如 Mobx、Redux等)適用于管理客戶端狀態,但它們并不關心客戶端是如何異步請求遠端數據的,所以他們并不適合處理異步的、來自服務端的狀態。

而 React Query 就是為了解決服務端狀態帶來的上述問題而出現的,除此之外它還帶來了以下特性:

  1. 更方便地控制緩存
  2. 把對于相同數據的多個請求簡化成一個
  3. 在后臺更新過期數據
  4. 知道數據什么時候會「過期」
  5. 對于數據的變化盡可能快得做出響應
  6. 分頁查詢和懶加載等請求性能優化
  7. 管理服務器狀態的內存和垃圾回收
  8. 通過結構共享(structural sharing)來緩存查詢結果

請求中間態處理

 function Todos() {
   const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)

   if (isLoading) {
     return <span>Loading...</span>
   }

   if (isError) {
     return <span>Error: {error.message}</span>
   }

   // also status === 'success', but "else" logic works, too
   return (
     <ul>
       {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
       ))}
     </ul>
   )
 }

React query會自動把這些isLoading,isError請求中間態處理好,我們不必寫重復邏輯,另外配合Suspense提對一點對于loading場景的處理,Suspense也支持的不錯,特別是局部Loading,簡直Nice!

ReactQuery 的狀態管理

Fetch, cache and update data in your React and React Native applications all without touching any "global state".

官網對于React Query的簡述,注意global state,你會不解,為什么React Query明明是一個請求庫,跟數據狀態管理又有什么關系,甚至可以處做全局狀態管理

那是因為ReactQuery 會在全局維護一個服務端狀態樹,根據 Query key 去查找狀態樹中是否有可用的數據,如果有則直接返回,否則則會發起請求,并將請求結果以 Query key 為主鍵存儲到狀態樹中。

ReactQuery 就將我們所有的服務端狀態維護在全局,并配合它的緩存策略來執行數據的存儲和更新。借助于這樣的特性,我們就可以將所有跟服務端進行交互的數據從類似于 Redux 這樣的狀態管理工具中剝離,而全部交給 ReactQuery 來管理。

舉個例子:

import React from "react";
import { useQuery, queryCache } from "react-query";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <h1>Shared state using react-query</h1>
      <Comp1 />
      <Comp2 />
    </div>
  );
}

function useSharedState(key, initialValue) {
  const { data: state } = useQuery(key, () => queryCache.getQueryData(key), {
    initialData: initialValue
  });

  const setState = value => queryCache.setQueryData(key, value);

  return [state, setState];
}

function Comp1() {
  const [count, setCount] = useSharedState("count", 1);

  console.log("comp1 rendered");

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>add</button>
    </div>
  );
}

function Comp2() {
  const [count, setCount] = useSharedState("count", 2);

  console.log("comp2 rendered");

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>add</button>
    </div>
  );
}

上述方式是可以實現React Query狀態管理,但是有性能問題,其實本質還是利用Context透傳,我們知道Context處理prop drilling問題,但是有性能問題,詳情可查看這篇文章[ 精讀《React — 5 Things That Might Surprise You》]

不過令人費解的是官方強調ReactQuery 的狀態管理,但是在官網例子并沒有給出類似的例子,上述例子還是在官方的github倉庫翻到

作者說會在一個講座分析,后面我再深入研究,先留個坑

參考文獻

  • https://react-query.tanstack.com/quick-start
  • https://github.com/tannerlinsley/react-query/discussions/489
  • https://github.com/tannerlinsley/react-query/discussions/329
  • https://tkdodo.eu/blog/react-query-as-a-state-manager

https://mp.weixin.qq.com/s/p7vwzaahodUv-dP2RdXaZw

最多閱讀

iOS 性能檢測新方式?——AnimationHitches 7月以前  |  15752次閱讀
快速配置 Sign In with Apple 2年以前  |  5428次閱讀
APP適配iOS11 3年以前  |  4408次閱讀
App Store 審核指南[2017年最新版本] 3年以前  |  4238次閱讀
所有iPhone設備尺寸匯總 3年以前  |  4156次閱讀
使用 GPUImage 實現一個簡單相機 2年以前  |  3878次閱讀
開篇 關于iOS越獄開發 3年以前  |  3783次閱讀
在越獄的iPhone設置上使用lldb調試 3年以前  |  3702次閱讀
給數組NSMutableArray排序 3年以前  |  3628次閱讀
使用ssh訪問越獄iPhone的兩種方式 3年以前  |  3333次閱讀
UITableViewCell高亮效果實現 3年以前  |  3332次閱讀
關于Xcode不能打印崩潰日志 3年以前  |  3229次閱讀
使用ssh 訪問越獄iPhone的兩種方式 3年以前  |  3068次閱讀
為對象添加一個釋放時觸發的block 3年以前  |  2844次閱讀
使用最高權限操作iPhone手機 3年以前  |  2817次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>