プログラミング初心者のための入門ブログエンジニアが未来を切り開く。Component)パターンの理解と使用法:実際の例でユースケースをカバーこの広告は、90日以上更新していないブログに表示しています。最後に高階コンポーネントHigher-Order-Component(HOC)は、Reactの構成上の性質から派生した設計パターンです。フックが導入される前は、コンテキストや外部データサブスクリプションにアクセスするためにHOCが広く使用されていました。HOCを記述して理解するのは非常に複雑です。そのため、フックが導入されたとき、誰もがフックに切り替えたのも不思議な事ではありません。それでもHOCの概念は、クラスコンポーネントと関数コンポーネント両方に使用できるため、最新のReactの世界でも適用できます。Hookによって完全に解決されます。面接担当者は、候補者がReactのコンポーネントシステムにどのくらい精通しているかをよりよく理解するために、この質問をすることもあります。受験者は、HOCが概念的にどのようなもので、どのような問題を解決するのかを説明できる必要があります。知っておく必要があるという事です。前述した通り高階コンポーネントは理解するのが少し難しいです。したがって当記事では、HOCを深く理解するためにパターンをいつ使用するかを理解し、実際の例でさまざまなユースケースをカバーします。事前に、あなたがReactの基本的な基礎に、必ず精通している事を確認して下さい。Component)パターンは、中級者以上の開発者にとって適切な挑戦となります。複数のコンポーネントで同じロジックのコピーを作成する必要性を感じたことがあるかもしれません。Reactにはロジックを書き直さなくても複数のコンポーネント間でロジックを共有する方法があるのではないかと疑問に思われているかもしれません。そのような問題を処理するための高度な手法があります。HOC)と呼ばれる高度な手法です。APIの一部ではありません。HOCまたはHOFは単なる設計パターンです。これらは、共通コードの労力と必要性を軽減する方法を定義するために使用される用語でもあります。HOCの概念は、関数型プログラミングの概念である高階関数から来ています。-【コールバック関数と高階関数】使い方•重要性で解説していますので参照してください。高階関数とは何かが分かっている前提で進めさせて頂きます。HOF)は、新しい技術ではありません。これは、関数を引数として受け取り(通常は特別な動作で拡張された)関数を返す関数です。高階関数です、至ってシンプルです。10これは、ご存知の通りmap()メソッドは関数を引数として受け入れるため、高階関数です。同様に、配列filter()、find()、reduce()の組み込み関数も高階関数です。12HOFを使用すると、JavaScriptで関数型プログラミングができることがわかります。関数型プログラミングでHOFが重要な理由は、関数をまとめて作成できるからです。基本的な概念は同じです。HOCも高階関数と非常によく似た概念です。HOCはコンポーネントを操作します。HOCはコンポーネントを受け取り、新しいコンポーネントを返す関数です。言い換えれば、HOFの概念をReactコンポーネントに適用すると、HOCが得られるという事です。(HOC)の名前として広く使用されております。/>;高階コンポーネントは、任意のReactコンポーネントを入力コンポーネントとして受け取り、その拡張バージョンを出力コンポーネントとして返します。上記は拡張されていないバージョンです。これらのコンポーネントは、動的な子コンポーネントを受け入れますが、子コンポーネントを変更またはコピーしないため、純粋なコンポーネントとも呼ばれます。継承よりも合成を優先するというReactの性質に由来するパターンです。HOCのユースケースHOCの使用例には次のものがあります。Components(HOC)パターンは先述した通り、主に継承よりも構成を優先するReactの機能から来ています。これは、コンポーネントを入力パラメーターとして取り、新しいコンポーネントを返します。HOCは何らかの関数に渡されるコンポーネントと考えることができます。その関数は基本的に、その元のコンポーネントの拡張バージョンを返します。単純で分かりやすいHOCの例は下記となります。};}上記のHOCは、パラメータとしてReactコンポーネントのWrappedComponentを取ります。新しいReactコンポーネントを返します。子として含まれている事になります。上記では、クラスコンポーネントをラッパーとして使用しています。下記では、関数コンポーネントをラッパーとして使用する例です。}}HOCは以下のように呼び出すことができます。HOCを使用して新しいコンポーネントを作成します。);};ですが、作成したばかりのHOCは、コンポーネントに対して何もしません。ラッパーコンポーネントで囲まれた同じコンポーネントを返すだけです。現在では、あまり意味がありません。HOCは、関数またはデータを使用してコンポーネントを拡張することです。コンポーネントの拡張コンポーネントを拡張するHOCの例を見てみましょう。それを行う方法は、コンポーネントにPropsを追加することです。値がTaroでnameのPropsを追加しましょう。};}上記ではHOCの定義です。simpleHOCという名前の関数を定義しています。この関数は引数としてWrappedComponent(ラップするコンポーネント)を受け取ります。HOCは新しいクラスコンポーネントを返します。この新しいクラスコンポーネントはrenderメソッドを持ち、WrappedComponentをラップしています。name="Taro"というpropsを追加しています。<h1>Hello,{name}</h1>;};上記では、Greetingという関数コンポーネントを定義しています。このコンポーネントは引数としてnameを受け取り、その値を表示するシンプルな挨拶文を返します。);};上記では、simpleHOCにGreetingコンポーネントが渡され、HOCによってラップされた新しいコンポーネントが生成されます。simpleHOCはGreetingコンポーネントを受け取り、それをラップして新しいコンポーネントを返すHOCです。したがって、simpleHOC(Greeting)は、`Greeting{コンポーネントが拡張された結果の新しいコンポーネントです。その新しいコンポーネントはNewComponentとして定義され、Greetingコンポーネントの機能に加えて、HOCが追加したpropsや振る舞いを持っています。したがって、NewComponentはGreetingコンポーネントを拡張した結果のコンポーネントです。HOCによって機能が追加され、新しいコンポーネントが生成されたものです。Propsを作成する必要はありません。Propsはなるべく作成しない方が望ましいです。以下は関数コンポーネントのラッパーです。CodePen.高階コンポーネントは、コンポーネントを別のコンポーネントに変換します。HOCはコンポーネントを変更しないことに注意してください。その代わりに、HOCは元のコンポーネントをコンテナーコンポーネントでラップすることによって構成します。HOCは、副作用のない純粋な関数となっています。新しい機能やデータでコンポーネントを強化するために使用される強力な概念となっています。HOCを使用すると、アプリケーションで重複するロジックの量を簡単に減らすことができます。他の例を示しましょう。Layout.jsファイルを作成します。withLayout;HOCの名前をwithという単語で始めるのは慣例です。Home.jsとAbout.jsファイルを作成します。withLayout(ComponentName)とするだけです。withLayout(AboutPage);ほとんどの場合、元のコンポーネントが定義された後、エクスポート時にコンポーネントにHOCを適用します。コンポーネントを任意の数のHOCでラップできます。したがって、HOCをコンポーネントに適用するのは非常に簡単です。必要なHOC関数をインポートして、元のコンポーネントをラップするだけです。CodePen.HOCを使用しない場合、下記のように両方のファイルでヘッダーとフッターのロジックを繰り返し使用します。);};上記は非常に望ましくありません。HOCを使用すれば、ロジックを美しく再利用できます。コンポーネントを複数のHOCでラップしたい場合は下記のように推奨します。withTitle)(App);これで2つのHOCを一緒にラップ可能です。ここで、気付いたかもしれませんね。これらは、カスタムフックを使う方が良いのではないかと思いませんか?そうです、その通りです。カスタムフックは、HOC(高階コンポーネント)と比較していくつかの利点を提供します。重複したロジックをカスタムフックにまとめることで、コードの可読性が向上し、ネストが深くなることもありません。カスタムフックは関数なので、プロパティの転送やライフサイクルの管理の問題も回避できます。HOCとカスタムフックは異なるユースケースに使用されます。HOCはコンポーネントの拡張やラッピングに適しており、カスタムフックはロジックの再利用に適しています。どちらを使うかは、具体的な要件やコードの構造によって異なる場合があります。フックのHOCuseStateフックを使用して関数コンポーネントを強化し、ラップされたコンポーネントがHOCによって提供されるカウンターをインクリメントできるようにする方法を示します。HOC自身はcountとsetCountフックをPropsとしてそのラップされたコンポーネントに注入します。CodePen.Wrappedコンポーネントはcountによって供給されるようにインクリメントできるようになります。関数コンポーネントはクラスコンポーネントよりも、よりシンプルです。クラスコンポーネントのHOCから切り替えを行うことができるより好ましいアプローチとなる可能性があります。ここまでは単純な例でした。HOCは通常、単一の引数としてコンポーネントを受け取りますが、JavaScriptの柔軟な関数機能により、より多くの情報や制御を提供するために外部からさらに引数を受け取ることもできます。強化されラップされたコンポーネントを返すために使用される別の関数を返す関数を提供する場合です。HOCに別の機能を追加する、追加構成です。=>これは、Reduxがconnect()行うことに似ております。HOCが高度な手法と言われているのが分かります。高階コンポーネントがコンポーネントのみを引数として取る場合、実装の詳細に関連するすべてのことは高階コンポーネント自体によって決定されます。JavaScriptには関数があるため外部からより多くの情報を引数として渡して、より多くの制御を得ることができます。options?)HOFはHOCの単一引数を保持します。上記のconnect()のメソッドでは、実際には4つのオプションのパラメータがあることがわかります。これらの機能追加は、HOC自体が単一引数関数のままであり、HOC同士を結合(組み合わせる)際に簡単に作成しやすくするためでもあります。結局のところ、外部から高階コンポーネントを構成できるようにしたいときはいつでも、HOCを別の関数でラップし、構成オブジェクトとして1つの引数を提供します。HOCを別の関数でラップして構成オブジェクトとして1つの引数を提供する例を示します。withConfig(config)(MyComponent);より複雑になってきました、上記の例では、最初のwithLogging関数は、コンポーネントをラップしてログを出力するHOCです。これは単一の引数としてコンポーネントを受け取り、新しいラップされたコンポーネントを返します。次に、withConfig関数は構成オブジェクトとして1つの引数を受け取るHOCです。この関数は、外部から構成オブジェクトを受け取り、それを参照する新しいHOCを返します。この新しいHOCは、コンポーネントをラップしてログを出力し、さらに構成オブジェクトを表示します。最後に、MyComponentは単純なコンポーネントです。最初のHOCを使用してMyComponentをラップしたEnhancedComponent1を作成し、ログを出力します。}を提供してMyComponentをラップしたEnhancedComponent2を作成し、ログと構成オブジェクトを出力します。このように、HOCを別の関数でラップし、構成オブジェクトとして1つの引数を提供することで、HOCの機能を柔軟に構成することができます。分かりやすく言いますと、HOCパターンの引数にHOFとして拡張し構成しています。HOFを使用すると、HOCの機能を動的に構成することが可能です。HOFは外部から引数を受け取り、それを参照する新しいHOCを生成します。この新しいHOCは、引数に基づいて機能を変更または拡張することができます。このようにして、HOCパターンを拡張するためにHOFを使用することができます。HOFはHOCの機能を柔軟に構成するための一つの手法として利用されます。フックとHOCについて以前は、様々な機能をクラスコンポーネントでしか使用できなかったため、それらに配置したロジックを再利用するには、高階コンポーネント(HOC)が必要でした。フックの導入によりカスタムフックを作成することで、以前のHOCからのデータフェッチロジックをほぼ同じ方法で再利用可能です。HOCを使用すると、2つのHOCが同じ名前のPropsを渡すと、厄介になります。フックはこれを解決します。Hooksからの出力として得られる変数の名前を変更することで、名前の衝突を回避します。HOCを使用する場合、HOCが内部でPropsに同じ名前を使用している可能性があることに注意する必要があります。useContext、useReducerフックおよびHOCパターンと共存させ使用して、ステートレスコンポーネントの記述方法を変更する必要のないReduxのようなパターンを作成することも可能です。紛らわしいクラス(thisキーワード、再利用可能にするのは非常に難しい)注意点HOCでは、以下に注意してください。・コンポーネントを変更または修正することはありません、新しいものを作成します。HOCは、コードを再利用するためのコンポーネントを構成するために使用されます。HOCは純粋関数です、つまり副作用がないということなので、新しいコンポーネントのみを返します。HOCを使用してはならない注意事項もあります。renderメソッド内でHOCを使用しないでください。NewComponentはレンダリングごとに新しいバージョンが作成されます。simpleHOC(Greeting);//そのため、サブツリー全体が毎回アンマウントされることになります。<NewComponent/>;}これは、基本的にHOCをベースコンポーネントに適用するたびに、新しい拡張コンポーネントが返されるためです。renderメソッド内でHOCを使用している場合、HOCのIDをrender全体で保持できず、アプリ全体のパフォーマンスに影響します。その問題はパフォーマンスだけではありません。コンポーネントを再マウントすると、そのコンポーネントとそのすべての子コンポーネントの状態が失われます。代わりに、結果のコンポーネントが一度だけ作成されるように、コンポーネント定義の外でHOCを適用する必要があります。静的メソッドをコピーする必要があるReactコンポーネントで静的メソッドを使用しても、特にHOCを適用したい場合には役に立ちません。HOCをコンポーネントに適用すると、新しい拡張コンポーネントが返されます。新しいコンポーネントには、元のコンポーネントの静的メソッドは含まれておりません。その問題を解決する場合は、あるパッケージを使用しなければいけません。すべての非React静的メソッドを自動的にコピーする必要があります。すべてのPropsをラップされたコンポーネントに渡したい場合があります。refはパススルーされないため注意が必要です。これは、refが実際にはPropsではないためですkeyのように、Reactによって特別に処理されます。HOCは読みにくく、理解しにくいものです。HOC呼び出しを読み取ってメソッドの機能を判断することはできません。データを受け取って返す構造を観察することができないため、デバッグやエラーの修復のコストが増加します。多数のHOCがある場合は、名前空間の競合が発生する可能性があります。HOCが親コンポーネントからPropsを受け取るときとコンポーネントを拡張するときは、2つの方法で引数を受け取る2つの方法があります。フックによってどのように解決されるかを理解する事が重要です。フックとして置き換える前に、それぞれの長所と短所を明確に理解し、プロジェクトに最適なソリューションを見つけることが重要です。フックを積極的に採用しています。関数コンポーネントとReactフックは、Reactを操作するための推奨される方法ですが、フックが必ずしも特効薬であるとは限りません。ReactフックとHOCには、それぞれ独自の機能があります。基本を理解したら、さまざまなコンポーネント間で共有できる機能を抽象化することで、概念を活用できます。カスタムフックだけを単独で使用する事も、また、HOCパターンはすべて共存できると思います。Propsをより効率的に使用するための適切なツールを取得できます。テスト、再利用性、およびロジックの明示性によって、時には主流ではなく、より適したものを決定する必要があります。本日は以上となります。最後まで読んで頂きありがとうございます。17:56ご訪問いただきありがとうございます。私はdeve.Kと申します。現役のReact.jsプログラマーでありWebアプリ開発に携わっています。当ブログでは、ITやプログラミングの学習を目的としたブログを運営しています。専門分野はJS/TS/Node.js/React/Next.jsです。質問や相談、または学びたい分野のリクエストなどがありましたら、お気軽にお問い合わせください。このブログでは、ITやプログラミングに関する情報を分かりやすく、かつ実践的な内容でお届けしています。皆様にとって有益な情報を提供し、より良い学習体験を得ていただけるよう、精一杯の努力をしてまいります。今後ともどうぞよろしくお願いいたします。[deve.K].はてなブログをはじめよう!dev04Kさんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。