useSWRのパフォーマンスを損なわずカスタムフックを作成する


useSWRはコンポーネントによって使用されている状態のみを更新します。

https://swr.vercel.app/docs/advanced/performance#dependency-collection

useSWRを使って、dataisValidatingを渡す以下のようなカスタムフックを作ると、このメリットが損なわれてしまいます。

import useSWR, { SWRConfiguration } from 'swr'
import { get, post } from './fetcher'
import { User } from './types'

const useUser = (userId: string, option?: SWRConfiguration) => {
  const { mutate, ...other } = useSWR<User>(`/users/${userId}`, get, option)

  const update = (newUserValue: User) =>
    mutate(async prevUser => {
      post(`/users/${userId}`, newUserValue)
      return newUserValue
    }, false)
    
   return { mutate, ...other, update }
}

このようなカスタムフックだと、updateしか使わないコンポーネントでも、dataが更新されるたびレンダリングが発生します。さらにotherにはisValidatingやerrorも含まれているのでこれらの値が更新されるたびにもレンダリングします。(計4回ほど)

これを回避するためには、スプレッド構文を使わずにObject.assignで返します。

import useSWR, { SWRConfiguration } from 'swr'
import { get, post } from './fetcher'
import { User } from './types'

const useUser = (userId: string, option?: SWRConfiguration) => {
  const swrResponse = useSWR<User>(`/users/${userId}`, get, option)
  const { mutate } = swrResponse

  const update = (newUserValue: User) =>
    mutate(async prevUser => {
      post(`/users/${userId}`, newUserValue)
      return newUserValue
    }, false)
    
   return Object.assign(swrResponse, { update })
}

本当はupdateもメモ化していたほうがいいでしょうね。