Skip to content
开发文档
中间件

中间件

¥Middleware

💡

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

¥Upgrade to the latest version (≥ 1.0.0) to use this feature.

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

¥The middleware feature is a new addition in SWR 1.0 that enables you to execute logic before and after SWR hooks.

用法

¥Usage

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

¥Middleware receive the SWR hook and can execute logic before and after running it. If there are multiple middleware, each middleware wraps the next middleware. The last middleware in the list will receive the original SWR hook useSWR.

API

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

¥Notes: The function name shouldn't be capitalized (e.g. myMiddleware instead of MyMiddleware) or React lint rules will throw Rules of Hook error

TypeScript (opens in a new tab)

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

¥You can pass an array of middleware as an option to SWRConfig or useSWR:

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

扩展

¥Extend

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

¥Middleware will be extended like regular options. For example:

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

相当于:

¥is equivalent to:

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

多个中间件

¥Multiple Middleware

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

¥Each middleware wraps the next middleware, and the last one just wraps the SWR hook. For example:

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

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

¥The order of middleware executions will be a → b → c, as shown below:

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

示例

¥Examples

请求日志器

¥Request Logger

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

¥Let's build a simple request logger middleware as an example. It prints out all the fetcher requests sent from this SWR hook. You can also use this middleware for all SWR hooks by adding it to SWRConfig.

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 键输出到控制台:

¥Every time the request is fired, it outputs the SWR key to the console:

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

保留之前的结果

¥Keep Previous Result

有时你希望 useSWR 返回的数据是 "laggy"。即使键发生变化,你仍然希望它返回以前的结果,直到加载新数据。

¥Sometimes you want the data returned by useSWR to be "laggy". Even if the key changes, you still want it to return the previous result until the new data has loaded.

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

¥This can be built as a laggy middleware together with useRef. In this example, we are also going to extend the returned object of the useSWR hook:

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 钩子来延迟时,你可以使用这个中间件:

¥When you need a SWR hook to be laggy, you can then use this middleware:

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

序列化对象键

¥Serialize Object Keys

💡

从 SWR 1.1.0 开始,类似对象的键将在后台自动序列化。

¥Since SWR 1.1.0, object-like keys will be serialized under the hood automatically.

⚠️

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

¥In older versions (< 1.1.0), SWR shallowly compares the arguments on every render, and triggers revalidation if any of them has changed. If you are passing serializable objects as the key. You can serialize object keys to ensure its stability, a simple middleware can help:

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] }}>

你无需担心对象可能会在渲染之间发生变化。它总是序列化为相同的字符串,并且获取器仍然会接收这些对象参数。

¥You don’t need to worry that object might change between renders. It’s always serialized to the same string, and the fetcher will still receive those object arguments.

💡

此外,你可以使用 fast-json-stable-stringify (opens in a new tab) 等库来代替 JSON.stringify — 更快、更稳定。

¥Furthermore, you can use libs like fast-json-stable-stringify (opens in a new tab) instead of JSON.stringify — faster and stabler.