跳至内容
文档
中间件

中间件

💡

升级到最新版本 (≥ 1.0.0) 以使用此功能。

中间件功能是 SWR 1.0 中的新增功能,它使您能够在 SWR 钩子之前和之后执行逻辑。

用法

中间件接收 SWR 钩子,可以在运行它之前和之后执行逻辑。如果有多个中间件,每个中间件都会包装下一个中间件。列表中的最后一个中间件将接收原始 SWR 钩子 useSWR

API

注意:函数名称不应大写(例如 myMiddleware 而不是 MyMiddleware)否则 React lint 规则会抛出 Rules of Hook 错误

TypeScript (在新标签页中打开)

function myMiddleware (useSWRNext) {
  return (key, fetcher, config) => {
    // Before hook runs...
 
    // Handle the next middleware, or the `useSWR` hook if this is the last one.
    const swr = useSWRNext(key, fetcher, config)
 
    // After hook runs...
    return swr
  }
}

您可以将中间件数组作为选项传递给 SWRConfiguseSWR

<SWRConfig value={{ use: [myMiddleware] }}>
 
// or...
 
useSWR(key, fetcher, { use: [myMiddleware] })

扩展

中间件将像常规选项一样扩展。例如

function Bar () {
  useSWR(key, fetcher, { use: [c] })
  // ...
}
 
function Foo() {
  return (
    <SWRConfig value={{ use: [a] }}>
      <SWRConfig value={{ use: [b] }}>
        <Bar/>
      </SWRConfig>
    </SWRConfig>
  )
}

等同于

useSWR(key, fetcher, { use: [a, b, c] })

多个中间件

每个中间件都包装下一个中间件,最后一个中间件只包装 SWR 钩子。例如

useSWR(key, fetcher, { use: [a, b, c] })

中间件执行顺序将为 a → b → c,如下所示

enter a
  enter b
    enter c
      useSWR()
    exit  c
  exit  b
exit  a

示例

请求日志记录器

让我们构建一个简单的请求日志记录器中间件作为示例。它打印出从这个 SWR 钩子发出的所有 fetcher 请求。您还可以通过将其添加到 SWRConfig 中,将此中间件用于所有 SWR 钩子。

function logger(useSWRNext) {
  return (key, fetcher, config) => {
    // Add logger to the original fetcher.
    const extendedFetcher = (...args) => {
      console.log('SWR Request:', key)
      return fetcher(...args)
    }
 
    // Execute the hook with the new fetcher.
    return useSWRNext(key, extendedFetcher, config)
  }
}
 
// ... inside your component
useSWR(key, fetcher, { use: [logger] })

每次发出请求时,它都会将 SWR 键输出到控制台

SWR Request: /api/user1
SWR Request: /api/user2

保留先前结果

有时您希望 useSWR 返回的数据“滞后”。即使键发生变化,您仍然希望它返回先前的结果,直到新数据加载完毕。

这可以与 useRef 一起构建为一个滞后中间件。在这个例子中,我们还将扩展 useSWR 钩子返回的对象

import { useRef, useEffect, useCallback } from 'react'
 
// This is a SWR middleware for keeping the data even if key changes.
function laggy(useSWRNext) {
  return (key, fetcher, config) => {
    // Use a ref to store previous returned data.
    const laggyDataRef = useRef()
 
    // Actual SWR hook.
    const swr = useSWRNext(key, fetcher, config)
 
    useEffect(() => {
      // Update ref if data is not undefined.
      if (swr.data !== undefined) {
        laggyDataRef.current = swr.data
      }
    }, [swr.data])
 
    // Expose a method to clear the laggy data, if any.
    const resetLaggy = useCallback(() => {
      laggyDataRef.current = undefined
    }, [])
 
    // Fallback to previous data if the current data is undefined.
    const dataOrLaggyData = swr.data === undefined ? laggyDataRef.current : swr.data
 
    // Is it showing previous data?
    const isLagging = swr.data === undefined && laggyDataRef.current !== undefined
 
    // Also add a `isLagging` field to SWR.
    return Object.assign({}, swr, {
      data: dataOrLaggyData,
      isLagging,
      resetLaggy,
    })
  }
}

当您需要一个 SWR 钩子滞后时,您可以使用此中间件

const { data, isLagging, resetLaggy } = useSWR(key, fetcher, { use: [laggy] })

序列化对象键

💡

从 SWR 1.1.0 开始,对象键将自动在幕后被序列化。

⚠️

在旧版本 (< 1.1.0) 中,SWR 浅层 比较每次渲染时的参数,如果任何参数发生变化,则触发重新验证。如果您将可序列化对象作为键传递。您可以序列化对象键以确保其稳定性,一个简单的中间件可以帮助您

function serialize(useSWRNext) {
  return (key, fetcher, config) => {
    // Serialize the key.
    const serializedKey = Array.isArray(key) ? JSON.stringify(key) : key
 
    // Pass the serialized key, and unserialize it in fetcher.
    return useSWRNext(serializedKey, (k) => fetcher(...JSON.parse(k)), config)
  }
}
 
// ...
useSWR(['/api/user', { id: '73' }], fetcher, { use: [serialize] })
 
// ... or enable it globally with
<SWRConfig value={{ use: [serialize] }}>

您不必担心对象可能会在渲染之间发生变化。它始终被序列化为同一个字符串,并且 fetcher 仍然会收到那些对象参数。

💡

此外,您可以使用像 fast-json-stable-stringify (在新标签页中打开) 这样的库来代替 JSON.stringify - 更快更稳定。