コンテンツへスキップ
ドキュメント
ミドルウェア

ミドルウェア

💡

この機能を使用するには、最新バージョン (≥ 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
  }
}

SWRConfig または useSWR にミドルウェアの配列をオプションとして渡すことができます。

<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 フックから送信されたすべてのフェッチャリクエストを出力します。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] }}>

レンダリング間でオブジェクトが変更されることを心配する必要はありません。常に同じ文字列にシリアライズされ、フェッチャは引き続きこれらのオブジェクト引数を受け取ります。

💡

さらに、JSON.stringify の代わりに fast-json-stable-stringify (新しいタブで開きます) などのライブラリを使用できます — より高速で安定しています。