日本語

useMemo、useCallback、React.memoを使用してReactアプリケーションのパフォーマンスを最適化するための包括的なガイド。不要な再レンダリングを防ぎ、ユーザーエクスペリエンスを向上させます。

Reactのパフォーマンス最適化: useMemo、useCallback、React.memoをマスターする

Reactは、ユーザーインターフェースを構築するための人気のあるJavaScriptライブラリであり、コンポーネントベースのアーキテクチャと宣言的なスタイルで知られています。しかし、アプリケーションの複雑さが増すにつれて、パフォーマンスが懸念事項になる可能性があります。コンポーネントの不要な再レンダリングは、パフォーマンスの低下と貧弱なユーザーエクスペリエンスにつながる可能性があります。幸いなことに、Reactには、useMemouseCallbackReact.memoなど、パフォーマンスを最適化するためのいくつかのツールが用意されています。このガイドでは、これらの手法を詳しく掘り下げ、実用的な例と実行可能な洞察を提供して、高性能なReactアプリケーションを構築するのに役立ちます。

Reactの再レンダリングについて

最適化手法に入る前に、Reactで再レンダリングが発生する理由を理解することが重要です。コンポーネントの状態またはpropsが変更されると、Reactはそのコンポーネントとその子コンポーネントの再レンダリングをトリガーします。Reactは仮想DOMを使用して実際のDOMを効率的に更新しますが、過剰な再レンダリングは、特に複雑なアプリケーションでは、パフォーマンスに影響を与える可能性があります。製品価格が頻繁に更新されるグローバルeコマースプラットフォームを想像してみてください。最適化しないと、小さな価格変更でも製品リスト全体の再レンダリングがトリガーされ、ユーザーのブラウジングに影響を与える可能性があります。

コンポーネントが再レンダリングされる理由

パフォーマンス最適化の目標は、不要な再レンダリングを防ぎ、コンポーネントが実際にデータが変更された場合にのみ更新されるようにすることです。株式市場分析のためのリアルタイムデータ可視化を含むシナリオを検討してください。チャートコンポーネントが軽微なデータ更新ごとに不必要に再レンダリングされると、アプリケーションは応答しなくなります。再レンダリングを最適化することで、スムーズで応答性の高いユーザーエクスペリエンスが保証されます。

useMemoの紹介: 高価な計算のメモ化

useMemoは、計算の結果をメモ化するReactフックです。メモ化は、高価な関数呼び出しの結果を保存し、同じ入力が再度発生したときにそれらの結果を再利用する最適化手法です。これにより、関数を不必要に再実行する必要がなくなります。

useMemoを使用する場合

useMemoの仕組み

useMemoは2つの引数を取ります。

  1. 計算を実行する関数。
  2. 依存関係の配列。

関数は、配列内の依存関係の1つが変更された場合にのみ実行されます。それ以外の場合、useMemoは以前にメモ化された値を返します。

例: フィボナッチ数列の計算

フィボナッチ数列は、計算集約的な計算の古典的な例です。useMemoを使用してn番目のフィボナッチ数を計算するコンポーネントを作成しましょう。


import React, { useState, useMemo } from 'react';

function Fibonacci({ n }) {
  const fibonacciNumber = useMemo(() => {
    console.log('Calculating Fibonacci...'); // Demonstrates when the calculation runs
    function calculateFibonacci(num) {
      if (num <= 1) {
        return num;
      }
      return calculateFibonacci(num - 1) + calculateFibonacci(num - 2);
    }
    return calculateFibonacci(n);
  }, [n]);

  return 

Fibonacci({n}) = {fibonacciNumber}

; } function App() { const [number, setNumber] = useState(5); return (
setNumber(parseInt(e.target.value))} />
); } export default App;

この例では、calculateFibonacci関数は、n propが変更された場合にのみ実行されます。useMemoがない場合、関数はFibonacciコンポーネントの再レンダリングごとに実行されます。nが同じままであってもです。これがグローバルな金融ダッシュボードで発生することを想像してください。市場のすべてのティックが完全に再計算され、大きな遅延が発生します。useMemoはそれを防ぎます。

useCallbackの紹介: 関数のメモ化

useCallbackは、関数をメモ化する別のReactフックです。これにより、レンダリングごとに新しい関数インスタンスが作成されるのを防ぎます。これは、コールバックを子コンポーネントにpropsとして渡す場合に特に役立ちます。

useCallbackを使用する場合

useCallbackの仕組み

useCallbackは2つの引数を取ります。

  1. メモ化する関数。
  2. 依存関係の配列。

関数は、配列内の依存関係の1つが変更された場合にのみ再作成されます。それ以外の場合、useCallbackは同じ関数インスタンスを返します。

例: ボタンクリックの処理

コールバック関数をトリガーするボタンを含むコンポーネントを作成しましょう。useCallbackを使用してコールバック関数をメモ化します。


import React, { useState, useCallback } from 'react';

function Button({ onClick, children }) {
  console.log('Button re-rendered'); // Demonstrates when the Button re-renders
  return ;
}

const MemoizedButton = React.memo(Button);

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('Button clicked');
    setCount((prevCount) => prevCount + 1);
  }, []); // Empty dependency array means the function is only created once

  return (
    

Count: {count}

Increment
); } export default App;

この例では、依存関係配列が空であるため、handleClick関数は1回だけ作成されます。count状態の変更によりAppコンポーネントが再レンダリングされると、handleClick関数は同じままです。React.memoでラップされたMemoizedButtonコンポーネントは、propsが変更された場合にのみ再レンダリングされます。onClick prop(handleClick)が同じままであるため、Buttonコンポーネントは不必要に再レンダリングされません。インタラクティブな地図アプリケーションを想像してみてください。ユーザーが操作するたびに、数十のボタンコンポーネントが影響を受ける可能性があります。useCallbackがないと、これらのボタンは不必要に再レンダリングされ、遅延が発生します。useCallbackを使用すると、よりスムーズな操作が保証されます。

React.memoの紹介: コンポーネントのメモ化

React.memoは、関数コンポーネントをメモ化する高階コンポーネント(HOC)です。propsが変更されていない場合、コンポーネントが再レンダリングされるのを防ぎます。これは、クラスコンポーネントのPureComponentに似ています。

React.memoを使用する場合

React.memoの仕組み

React.memoは関数コンポーネントをラップし、前のpropsと次のpropsを浅く比較します。propsが同じ場合、コンポーネントは再レンダリングされません。

例: ユーザープロフィールの表示

ユーザープロファイルを表示するコンポーネントを作成しましょう。ユーザーのデータが変更されていない場合、React.memoを使用して不要な再レンダリングを防ぎます。


import React from 'react';

function UserProfile({ user }) {
  console.log('UserProfile re-rendered'); // Demonstrates when the component re-renders
  return (
    

Name: {user.name}

Email: {user.email}

); } const MemoizedUserProfile = React.memo(UserProfile, (prevProps, nextProps) => { // Custom comparison function (optional) return prevProps.user.id === nextProps.user.id; // Only re-render if the user ID changes }); function App() { const [user, setUser] = React.useState({ id: 1, name: 'John Doe', email: 'john.doe@example.com', }); const updateUser = () => { setUser({ ...user, name: 'Jane Doe' }); // Changing the name }; return (
); } export default App;

この例では、MemoizedUserProfileコンポーネントは、user.id propが変更された場合にのみ再レンダリングされます。userオブジェクトの他のプロパティ(名前やメールなど)が変更された場合でも、IDが異なる場合を除き、コンポーネントは再レンダリングされません。`React.memo`内のこのカスタム比較関数により、コンポーネントが再レンダリングされるタイミングを細かく制御できます。常に更新されるユーザープロファイルを持つソーシャルメディアプラットフォームを検討してください。`React.memo`がない場合、ユーザーのステータスまたはプロフィール写真を変更すると、コアユーザーの詳細が同じままであっても、プロファイルコンポーネントが完全に再レンダリングされます。`React.memo`を使用すると、ターゲットを絞った更新が可能になり、パフォーマンスが大幅に向上します。

useMemo、useCallback、React.memoの組み合わせ

これらの3つの手法は、組み合わせて使用​​すると最も効果的です。useMemoは高価な計算をメモ化し、useCallbackは関数をメモ化し、React.memoはコンポーネントをメモ化します。これらの手法を組み合わせることで、Reactアプリケーションでの不要な再レンダリングの数を大幅に減らすことができます。

例: 複雑なコンポーネント

これらの手法を組み合わせる方法を示す、より複雑なコンポーネントを作成しましょう。


import React, { useState, useCallback, useMemo } from 'react';

function ListItem({ item, onUpdate, onDelete }) {
  console.log(`ListItem ${item.id} re-rendered`); // Demonstrates when the component re-renders
  return (
    
  • {item.text}
  • ); } const MemoizedListItem = React.memo(ListItem); function List({ items, onUpdate, onDelete }) { console.log('List re-rendered'); // Demonstrates when the component re-renders return (
      {items.map((item) => ( ))}
    ); } const MemoizedList = React.memo(List); function App() { const [items, setItems] = useState([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, ]); const handleUpdate = useCallback((id) => { setItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, text: `Updated ${item.text}` } : item ) ); }, []); const handleDelete = useCallback((id) => { setItems((prevItems) => prevItems.filter((item) => item.id !== id)); }, []); const memoizedItems = useMemo(() => items, [items]); return (
    ); } export default App;

    この例では:

    この手法の組み合わせにより、コンポーネントは必要な場合にのみ再レンダリングされるようになり、パフォーマンスが大幅に向上します。タスクのリストが常に更新、削除、並べ替えられている大規模なプロジェクト管理ツールを想像してみてください。これらの最適化がないと、タスクリストへのわずかな変更でも、カスケード再レンダリングがトリガーされ、アプリケーションの速度が低下し、応答しなくなります。useMemouseCallback、およびReact.memoを戦略的に使用することにより、アプリケーションは複雑なデータと頻繁な更新があっても、パフォーマンスを維持できます。

    その他の最適化手法

    useMemouseCallback、およびReact.memoは強力なツールですが、Reactのパフォーマンスを最適化するための唯一のオプションではありません。検討すべき追加の手法を次に示します。

    最適化のためのグローバルな考慮事項

    グローバルオーディエンス向けにReactアプリケーションを最適化する場合は、ネットワーク遅延、デバイスの機能、ローカリゼーションなどの要素を考慮することが重要です。いくつかのヒントを次に示します。

    結論

    Reactアプリケーションのパフォーマンスを最適化することは、スムーズで応答性の高いユーザーエクスペリエンスを提供するために重要です。useMemouseCallback、およびReact.memoなどの手法を習得し、グローバルな最適化戦略を検討することで、多様なユーザーベースのニーズを満たすようにスケーリングする高性能なReactアプリケーションを構築できます。パフォーマンスのボトルネックを特定し、これらの最適化手法を戦略的に適用するために、アプリケーションをプロファイリングすることを忘れないでください。時期尚早な最適化は行わないでください。最も大きな影響を与えることができる領域に焦点を当ててください。

    このガイドは、Reactのパフォーマンス最適化を理解し、実装するための確固たる基盤を提供します。Reactアプリケーションの開発を続ける際には、パフォーマンスを優先し、ユーザーエクスペリエンスを向上させるための新しい方法を継続的に模索することを忘れないでください。