Skip to content
Документация
Промежуточное ПО (Middleware)

Промежуточное программное обеспечение (ППО) (Middleware)

💡

Обновитесь до последней версии (≥ 1.0.0), чтобы использовать этот функционал.

Функционал ППО — это новое дополнение в SWR 1.0, которое позволяет вам выполнять логику до и после SWR хуков.

Использование

Промежуточное ПО получает SWR хук и может выполнять логику до и после его запуска. Если ППО несколько, каждый ППО оборачивает последующий. Последний ППО в списке получит исходный хук SWR — useSWR.

API

Примечание: Имя функции не должно быть написано с заглавной буквы (например, myMiddleware вместо MyMiddleware), иначе правила линтера React будут выдавать ошибку Rules of Hook

TypeScript (opens in a new tab)

function myMiddleware (useSWRNext) {
  return (key, fetcher, config) => {
    // До выполнения хука...
 
    // Обработка следующего ППО, или хука `useSWR`, если это последнее.
    const swr = useSWRNext(key, fetcher, config)
 
    // После выполнения хука...
    return swr
  }
}

Вы можете передать массив из нескольких ППО как опцию SWRConfig или useSWR:

<SWRConfig value={{ use: [myMiddleware] }}>
 
// или...
 
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, как показано ниже:

вход в  a
  вход в  b
    вход в  c
      useSWR()
    выход из  c
  выход из  b
выход из  a

Примеры

Регистратор запросов

Давайте в качестве примера создадим простой ППО — регистратора запросов. Он выводит все запросы fetcher-а, отправленные с этого хука SWR. Вы также можете использовать этот ППО для всех хуков SWR, добавив его в SWRConfig.

function logger(useSWRNext) {
  return (key, fetcher, config) => {
    // Добавим регистратор в исходный fetcher.
    const extendedFetcher = (...args) => {
      console.log('SWR запрос:', key)
      return fetcher(...args)
    }
 
    // Выполняем хук с новым fetcher-ом.
    return useSWRNext(key, extendedFetcher, config)
  }
}
 
// ... внутри вашего компонента
useSWR(key, fetcher, { use: [logger] })

Каждый раз, когда запрос запускается, он выводит ключ SWR в консоль:

SWR запрос: /api/user1
SWR запрос: /api/user2

Сохранение предыдущего результата

Иногда вы хотите, чтобы данные, возвращаемые useSWR, были «запаздывающими». Даже если ключ изменится, вы все равно хотите, чтобы он возвращал предыдущий результат, пока не загрузятся новые данные.

Это может быть построено как замедленное ППО используя useRef. В этом примере мы также собираемся расширить возвращаемый объект хука useSWR:

import { useRef, useEffect, useCallback } from 'react'
 
// Это ППО SWR для хранения данных даже при изменении ключа.
function laggy(useSWRNext) {
  return (key, fetcher, config) => {
    // Используйте ссылку для хранения ранее возвращённых данных.
    const laggyDataRef = useRef()
 
    // Фактический хук SWR.
    const swr = useSWRNext(key, fetcher, config)
 
    useEffect(() => {
      // Обновите ссылку если данные определены.
      if (swr.data !== undefined) {
        laggyDataRef.current = swr.data
      }
    }, [swr.data])
 
    // Предоставьте метод очистки запаздывающих данных, если таковые имеются.
    const resetLaggy = useCallback(() => {
      laggyDataRef.current = undefined
    }, [])
 
    // Возврат к предыдущим данным, если текущие данные не определены.
    const dataOrLaggyData = swr.data === undefined ? laggyDataRef.current : swr.data
 
    // Показывает предыдущие данные?
    const isLagging = swr.data === undefined && laggyDataRef.current !== undefined
 
    // Также добавьте поле `isLagging` в 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) => {
    // Сериализуйте ключ.
    const serializedKey = Array.isArray(key) ? JSON.stringify(key) : key
 
    // Передайте сериализованный ключ и десериализуйте его в fetcher-е.
    return useSWRNext(serializedKey, (k) => fetcher(...JSON.parse(k)), config)
  }
}
 
// ...
useSWR(['/api/user', { id: '73' }], fetcher, { use: [serialize] })
 
// ... или включите его глобально с помощью
<SWRConfig value={{ use: [serialize] }}>

Вам не нужно беспокоиться о том, что объект может измениться между рендерами. Он всегда сериализуется в одну и ту же строку, и fetcher по-прежнему получит эти аргументы объекта.

💡

Кроме того, вы можете использовать такие библиотеки, как fast-json-stable-stringify (opens in a new tab) вместо JSON.stringify — быстрее и стабильнее.