Skip to content
⚠️ This article was written in 2020. Some content may be outdated.

Reactのパフォーマンス最適化 memo と useMemo

React のパフォーマンス最適化(memo、useMemo)についてはコミュニティで何度も議論されてきましたが、バージョンアップに伴い、多くの結論を更新する必要があります。この記事では最新バージョンに基づいて再整理します。

はじめに

実際のプロジェクトでの使用法はもう少し複雑になります:

javascript
import { useState, useEffect, useCallback } from 'react'

function DataList({ endpoint, pageSize = 20 }) {
  const [data, setData] = useState([])
  const [page, setPage] = useState(1)
  const [loading, setLoading] = useState(false)

  const fetchData = useCallback(async () => {
    setLoading(true)
    try {
      const res = await fetch(`${endpoint}?page=${page}&size=${pageSize}`)
      setData(await res.json())
    } finally { setLoading(false) }
  }, [endpoint, page, pageSize])

  useEffect(() => { fetchData() }, [fetchData])

  return <div>{loading ? <Spinner /> : <List items={data} />}</div>
}

この方法により、コードのテスタビリティと拡張性が向上します。

ソースコード解析

以下は完全なサンプルです:

javascript
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.entryType === 'largest-contentful-paint') {
      reportMetric('LCP', entry.startTime)
    }
    if (entry.entryType === 'first-input') {
      reportMetric('FID', entry.processingStart - entry.startTime)
    }
  }
})
observer.observe({ entryTypes: ['largest-contentful-paint', 'first-input'] })

境界条件の処理に注意してください。これは本番環境で非常に重要です。

実際のシナリオへの応用

核心となるロジックを理解することが重要です:

javascript
import { useRef, useEffect, useState } from 'react'

function useIntersectionObserver(options = {}) {
  const [isVisible, setIsVisible] = useState(false)
  const ref = useRef(null)

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      setIsVisible(entry.isIntersecting)
    }, { threshold: 0.1, ...options })
    const el = ref.current
    if (el) observer.observe(el)
    return () => { if (el) observer.unobserve(el) }
  }, [])

  return [ref, isVisible]
}

パフォーマンス最適化は具体的なシナリオに応じて行う必要があり、すべての場合に過度な最適化が必要なわけではありません。

最適化のコツ

以下の方法で改善できます:

javascript
import { useReducer, useCallback } from 'react'

const initialState = { items: [], filter: '', sort: 'date' }

function reducer(state, action) {
  switch (action.type) {
    case 'SET_ITEMS': return { ...state, items: action.payload }
    case 'SET_FILTER': return { ...state, filter: action.payload }
    case 'ADD_ITEM': return { ...state, items: [...state.items, action.payload] }
    case 'REMOVE_ITEM': return { ...state, items: state.items.filter(i => i.id !== action.payload) }
    default: throw new Error(`Unknown: ${action.type}`)
  }
}

この方法はすでに本番環境で半年以上安定して稼働しており、実際に検証されています。

まとめ

  • コードサンプルは参考用です。実際のビジネスシナリオに応じて調整してください
  • React のパフォーマンス最適化(memo、useMemo)は銀の弾丸ではありません。プロジェクトの規模や技術スタックに応じて選択する必要があります
  • 基礎となる原理を理解することは、API を覚えることよりも重要です
  • 本番環境で使用する前に、必ず互換性の検証を行ってください
  • チームコラボレーションにおいては、約束事とドキュメントが技術自体よりも重要です

MIT Licensed