跳到內容
SWR 2.0 發佈

SWR 2.0 發佈

2022 年 12 月 9 日 作者: Shu DingJiachi LiuToru KobayashiYixuan Xu

我們很高興地宣布 SWR 2.0 的發布,這是一個受歡迎的 React 資料擷取函式庫,使元件能夠擷取、快取和修改資料,並使 UI 隨著時間的推移與資料的變化保持同步。

這個新版本包含許多改進和新功能,例如新的修改 API、改進的樂觀 UI 功能、新的 DevTools 以及對並行渲染的更好支援。我們要衷心感謝所有為這個版本做出貢獻的貢獻者和維護者。

修改和樂觀 UI

useSWRMutation

修改是資料擷取過程中重要的一部分。它們可讓您在本機和遠端變更資料。我們現有的 mutate API 可讓您手動重新驗證和修改資源。在 SWR 2.0 中,新的 Hook useSWRMutation 使用宣告式 API 更簡單地遠端變更資料。您可以使用 Hook 設定修改,然後稍後啟用它

import useSWRMutation from 'swr/mutation'
 
async function sendRequest(url, { arg }) {
  return fetch(url, {
    method: 'POST',
    body: JSON.stringify(arg)
  })
}
 
function App() {
  const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
 
  return (
    <button
      disabled={isMutating}
      onClick={() => trigger({ username: 'johndoe' })}
    >{
      isMutating ? 'Creating...' : 'Create User'
    }</button>
  )
}

上面的範例定義了一個會影響 '/api/user' 資源的 sendRequest 修改。與 useSWR 不同,useSWRMutation 不會在渲染時立即啟動請求。相反地,它會傳回一個 trigger 函式,稍後可以呼叫該函式來手動啟動修改。

當點擊按鈕時,將會呼叫 sendRequest 函式,並帶有額外的參數 { username: 'johndoe' }isMutating 的值將設定為 true,直到修改完成為止。

此外,這個新的 Hook 解決了您可能遇到的其他修改問題

  • 在修改資料時,以樂觀方式更新 UI
  • 當修改失敗時自動復原
  • 避免 useSWR 和相同資源的其他修改之間可能發生的任何競爭狀況
  • 在修改完成後,填入 useSWR 快取
  • ...

您可以閱讀文件或滾動瀏覽接下來的幾個部分,找到深入的 API 參考和範例。

樂觀 UI

樂觀 UI 是一種出色的模型,用於建立感覺快速且反應靈敏的網站;但是,它可能難以正確實作。SWR 2.0 新增了一些強大的選項,使其更容易。

假設我們有一個 API,可以將新的待辦事項新增到待辦事項清單中並傳送到伺服器

await addNewTodo('New Item')

在我們的 UI 中,我們使用 useSWR Hook 來顯示待辦事項清單,並帶有「新增項目」按鈕,該按鈕會觸發此請求並要求 SWR 透過 mutate() 重新擷取資料

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Display data */}</ul>
 
  <button onClick={async () => {
    await addNewTodo('New Item')
    mutate()
  }}>
    Add New Item
  </button>
</>

但是,await addNewTodo(...) 請求可能會非常慢。當它正在進行時,即使我們已經知道新清單的外觀,使用者仍然會看到舊清單。透過新的 optimisticData 選項,我們可以在伺服器回應之前,以樂觀的方式顯示新清單

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Display data */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('New Item'), {
      optimisticData: [...data, 'New Item'],
    })
  }}>
    Add New Item
  </button>
</>

SWR 會立即使用 optimisticData 值更新 data,然後將請求傳送到伺服器。請求完成後,SWR 將重新驗證資源以確保它是最新的。

與許多 API 一樣,如果 addNewTodo(...) 請求傳回伺服器的最新資料,我們也可以直接顯示該結果 (而不是開始新的重新驗證)!有一個新的 populateCache 選項,告訴 SWR 使用修改回應來更新本機資料

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Display data */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('New Item'), {
      optimisticData: [...data, 'New Item'],
      populateCache: true,
    })
  }}>
    Add New Item
  </button>
</>

同時,由於回應資料來自真實來源,我們不需要之後的另一次重新驗證,因此可以使用 revalidate 選項停用它

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Display data */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('New Item'), {
      optimisticData: [...data, 'New Item'],
      populateCache: true,
      revalidate: false,
    })
  }}>
    Add New Item
  </button>
</>

最後,如果 addNewTodo(...) 失敗並出現例外狀況,我們可以藉由將 rollbackOnError 設定為 true (這也是預設選項),復原我們剛設定的樂觀資料 ([...data, 'New Item'])。當這種情況發生時,SWR 將把 data 回滾到先前的值。

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Display data */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('New Item'), {
      optimisticData: [...data, 'New Item'],
      populateCache: true,
      revalidate: false,
      rollbackOnError: true,
    })
  }}>
    Add New Item
  </button>
</>

所有這些 API 也都在新的 useSWRMutation Hook 中支援。若要瞭解更多資訊,可以查看我們的文件。這裡有一個展示此行為的範例。

具有自動錯誤回滾的樂觀 UI

更新多個鍵值

全域 mutate API 現在接受篩選函數,您可以在其中更新或重新驗證特定鍵值。這對於使所有快取資料失效等使用案例很有幫助。若要瞭解更多資訊,您可以閱讀文件中的更新多個鍵值

import { mutate } from 'swr'
// Or from the hook if you have customized your cache provider:
// { mutate } = useSWRConfig()
 
// Mutate single resource
mutate(key)
 
// Mutate multiple resources and clear the cache (set to undefined)
mutate(
  key => typeof key === 'string' && key.startsWith('/api/item?id='),
  undefined,
  { revalidate: false }
)

SWR 開發者工具

SWRDevTools (在新分頁中開啟) 是一個瀏覽器擴充功能,可協助您除錯 SWR 快取和提取結果。請查看我們的 開發者工具 章節,瞭解如何在應用程式中使用開發者工具。

預載入資料

預載入資料可以大幅改善使用者體驗。如果您知道資源稍後會在應用程式中使用,您可以使用新的 preload API 來提早開始提取資料

import useSWR, { preload } from 'swr'
 
const fetcher = (url) => fetch(url).then((res) => res.json())
 
// You can call the preload function in anywhere
preload('/api/user', fetcher)
 
function Profile() {
  // The component that actually uses the data:
  const { data, error } = useSWR('/api/user', fetcher)
  // ...
}
 
export function Page () {
  return <Profile/>
}

在這個範例中,preload API 是在全域範圍中呼叫的。這表示我們在 React 開始渲染任何內容之前,就開始預載入資源。當 Profile 元件正在渲染時,資料可能已經可用。如果仍在進行中,useSWR Hook 將會重複使用正在進行的預載入請求,而不是開始新的請求。

在預載入可能會渲染的其他頁面的資料等情況下,也可以使用 preload API。有關使用 SWR 預先提取資料的更多資訊,請參閱這裡

isLoading

isLoadinguseSWR 回傳的新狀態,表示如果請求仍在進行中,且尚未載入任何資料。先前,isValidating 狀態同時表示初始載入狀態和重新驗證狀態,因此我們必須檢查 dataerror 是否為 undefined,以判斷是否為初始載入狀態。

現在,您可以直接使用 isLoading 值來渲染載入訊息,非常容易。

import useSWR from 'swr'
 
function Profile() {
  const { data, isLoading } = useSWR('/api/user', fetcher)
 
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

請注意,isValidating 仍然存在,因此您仍然可以使用它來顯示重新驗證的載入指示器。

📝

我們新增了新的了解 SWR 頁面,以描述 SWR 如何回傳值,包括 isValidatingisLoading 之間的差異,以及如何結合它們以改善使用者體驗。

保留先前的狀態

keepPreviousData 選項是一個新的新增功能,可讓您保留先前提取的資料。當您根據即時發生的使用者操作提取資料時,例如使用即時搜尋功能(其中資源的 key 持續變更)時,這可以極大地改善使用者體驗

function Search() {
  const [search, setSearch] = React.useState('');
 
  const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
    keepPreviousData: true
  })
 
  return (
    <div>
      <input
        type="text"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search..."
      />
 
      <div className={isLoading ? "loading" : ""}>
        {data?.products.map(item => <Product key={item.id} name={item.name} />)
      </div>
    </div>
  );
}
啟用 keepPreviousData 時,保留先前的搜尋結果

請查看 CodeSandbox (在新分頁中開啟) 上的程式碼,您可以在這裡閱讀更多相關資訊。

擴充組態

SWRConfig 現在可以接受函數值。當您有多個層級的 <SWRConfig> 時,內部會接收父層組態並回傳新的組態。此變更讓您可以在大型程式碼庫中更靈活地設定 SWR。更多資訊請參閱這裡

<SWRConfig
  value={parentConfig => ({
    dedupingInterval: parentConfig.dedupingInterval * 5,
    refreshInterval: 100,
  })}
>
  <Page />
</SWRConfig>

改善 React 18 支援

SWR 已更新其內部程式碼,以在 React 18 中使用 useSyncExternalStorestartTransition API。這些可確保在同時渲染 UI 時具有更強的一致性。此變更不需要任何使用者程式碼變更,所有開發人員都將直接受益。針對 React 17 及更早版本,則包含 Shims。

SWR 2.0 和所有新功能仍然與 React 16 和 17 相容。

移轉指南

提取器不再接受多個引數

key 現在會以單一引數傳遞。

- useSWR([1, 2, 3], (a, b, c) => {
+ useSWR([1, 2, 3], ([a, b, c]) => {
  assert(a === 1)
  assert(b === 2)
  assert(c === 3)
})

全域 Mutate 不再接受 getKey 函式

現在,如果您將函式傳遞給全域 mutate,它將會被用作篩選器。先前,您可以將回傳鍵值的函式傳遞給全域 mutate

- mutate(() => '/api/item') // a function to return a key
+ mutate('/api/item')       // to mutate the key, directly pass it

快取介面新增必要屬性 keys()

當您使用自己的快取實作時,快取介面現在需要一個 keys() 方法,該方法會返回快取物件中的所有鍵,類似於 JavaScript 的 Map 實例。

interface Cache<Data> {
  get(key: string): Data | undefined
  set(key: string, value: Data): void
  delete(key: string): void
+ keys(): IterableIterator<string>
}

變更快取內部結構

快取資料的內部結構將會是一個物件,其中包含所有目前狀態。

- assert(cache.get(key) === data)
+ assert(cache.get(key) === { data, error, isValidating })
 
// getter
- cache.get(key)
+ cache.get(key)?.data
 
// setter
- cache.set(key, data)
+ cache.set(key, { ...cache.get(key), data })
🚨

您不應該直接寫入快取,這可能會導致未定義的行為。

SWRConfig.default 已重新命名為 SWRConfig.defaultValue

SWRConfig.defaultValue 是用於存取預設 SWR 設定的屬性。

- SWRConfig.default
+ SWRConfig.defaultValue

類型 InfiniteFetcher 已重新命名為 SWRInfiniteFetcher

- import type { InfiniteFetcher } from 'swr/infinite'
+ import type { SWRInfiniteFetcher } from 'swr/infinite'

避免在伺服器端使用 Suspense

如果您想在伺服器端使用 SWR 的 suspense: true,包括在 Next.js 中進行預先渲染,那麼您必須透過 fallbackDatafallback 提供初始資料。目前,這意味著您不能在伺服器端使用 Suspense 來獲取資料。您的其他兩個選項是執行完全的客戶端資料獲取,或者讓您的框架為您獲取資料(就像 Next.js 中的 getStaticProps 所做的那樣)。

ES2018 作為建置目標

如果您想支援 IE 11,您必須在您的框架或打包器中將目標設定為 ES5。此變更在 SSR 上提高了效能,並保持了較小的 bundle 大小。

變更日誌

閱讀完整的變更日誌 在 GitHub 上 (在新分頁中開啟)

未來與感謝!

隨著 Next.js 13 (在新分頁中開啟) 的新發布,我們看到了許多令人興奮的新事物,以及 React 生態系統的典範轉移:React 伺服器元件 (在新分頁中開啟)、串流 SSR、非同步元件 (在新分頁中開啟) 以及 use hook (在新分頁中開啟)。其中許多都與資料獲取有關,而且其中一些與 SWR 有重疊的使用案例。

然而,SWR 專案的目標保持不變。我們希望它是一個輕量級、與框架無關、並且稍微有主見(即在焦點時重新驗證)的插入式函式庫。我們不打算成為一個標準的解決方案,而是希望專注於使 UX 更好的創新。同時,我們也在研究如何利用 React 的這些新功能來改進 SWR。

我們要感謝每一位 143 (在新分頁中開啟) 位貢獻者(+ 106 (在新分頁中開啟) 位文件貢獻者),以及幫助我們或提供回饋的人。特別感謝 Toru Kobayashi (在新分頁中開啟) 在 DevTools 和文件方面所做的所有工作——沒有你,我們無法完成!