2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

意図しないタイミングで大量に再レンダリングが発生

useEffectやuseMemoを使う際、複雑な構造のObjectやArray型を第2引数に指定すると意図しないタイミングで再レンダリングが走ってしまうことが多々あったため、深い比較(DeepCompare)をするためのCustomHookを作ってみました。

なぜ意図しないタイミングで再レンダリングされるのか?

以下の記事で丁寧にまとめられているため詳しい解説は割愛しますが、要するにReactHooksの内部で === での比較がされているのが原因でレンダリングが発生してしまうものを、今回はCustomHookの中で深い比較に変えて対応してしまおうという形です。

前提環境

  • react@17.0.2
  • 深い比較には fast-deep-equal@3.1.3 を使用
  • 複雑なデータをフロントで扱わざるを得ないシステムなため、useEffectやuseMemoには多次元配列や深くネストしたObjectを構わず指定していく

実際のコード

共通で使う関数

import * as React from 'react'
import * as deepEqual from 'fast-deep-equal'
import { DependencyList } from 'react'

// 以下ページのコードをそのまま利用
// https://stackoverflow.com/questions/54095994/react-useeffect-comparing-objects
function useDeepCompareMemorize(value: DependencyList[number]) {
  const ref = React.useRef()

  if (!deepEqual(value, ref.current)) {
    ref.current = value
  }

  return ref.current
}

deepEqualでの比較を用いたuseEffect

export function useDeepCompareEffect(
  callback: React.EffectCallback,
  dependencies: DependencyList
) {
  // eslint-disable-next-line react-hooks/exhaustive-deps
  React.useEffect(callback, dependencies.map(useDeepCompareMemorize))
}

deepEqualでの比較を用いたuseMemo

export function useDeepCompareMemo<T>(
  callback: () => T,
  dependencies: DependencyList
) {
  // eslint-disable-next-line react-hooks/exhaustive-deps
  return React.useMemo(callback, dependencies.map(useDeepCompareMemorize))
}

deepEqualでの比較を用いたuseEffectで、かつ初回の実行を無効にしたもの

export const useDeepCompareUpdateEffect = (
  callback: () => void,
  dependencies: DependencyList
) => {
  const isFirstRender = React.useRef(true)
  useDeepCompareEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false
      return
    }
    return callback()
  }, dependencies)
}

使い方

普段useEffectやuseMemoを使うのと同じように使います。

const obj = { hoge: 'hoge', fuga: 'fuga', nest: { hoge: 'hoge', fuga: 'fuga' } }

useDeepCompareEffect(() => {
  // do something
}, [obj])

useDeepCompareUpdateEffect(() => {
  // do something
}, [obj])

const value = useDeepCompareMemo(() => {
  // do something
}, [obj])


2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?