{"componentChunkName":"component---src-templates-docs-js","path":"/docs/higher-order-components.html","result":{"data":{"markdownRemark":{"html":"<p>Компонент вищого порядку (КВП) — це просунута технологія для повторного використання логіки компоненту. Сам по собі КВП не є частиною React API, але через композиційну природу компонентів він є розповсюдженним патерном проектування.</p>\n<p>Тобто, <strong>компонент вищого порядку — це функція, яка приймає компонент та повертає новий компонент.</strong></p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">const</span> EnhancedComponent <span class=\"token operator\">=</span> <span class=\"token function\">higherOrderComponent</span><span class=\"token punctuation\">(</span>WrappedComponent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Якщо звичайний компонент трасформує пропси у UI, то компонент вищого порядку трасформує один компонент у інший.</p>\n<p>КВП поширені у таких сторонніх бібліотеках, як <a href=\"https://github.com/reduxjs/react-redux/blob/master/docs/api/connect.md#connect\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"gatsby-code-text\">connect</code></a> у Redux, або <a href=\"http://facebook.github.io/relay/docs/en/fragment-container.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"gatsby-code-text\">createFragmentContainer</code></a> у Relay.</p>\n<p>У цьому розділі ми обговоримо чому компоненти вищого порядку корисні та як створювати їх власноруч.</p>\n<h2 id=\"use-hocs-for-cross-cutting-concerns\"><a href=\"#use-hocs-for-cross-cutting-concerns\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Використання КВП для перехресної функціональності </h2>\n<blockquote>\n<p><strong>Примітка</strong></p>\n<p>Раніше ми рекомендували міксини для реалізації перехресної функціональності. Але з часом ми з’ясували, що від них більше клопоту, ніж користі. <a href=\"/blog/2016/07/13/mixins-considered-harmful.html\">Дізнайтеся більше</a> про те, чому ми відмовилися від міксинів та як ви можете переписати існуючі компоненти.</p>\n</blockquote>\n<p>Зазвичай компоненти є основною одиницею повторного використання коду у React. Однак ви побачите, що вони не є відповідним рішенням низки проблем.</p>\n<p>Наприклад, ви маєте компонент <code class=\"gatsby-code-text\">CommentList</code>, який підписується на зовнішнє джерело даних, щоб відобразити список коментарів:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">CommentList</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">React<span class=\"token punctuation\">.</span>Component</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>props<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">handleChange</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// \"DataSource\" є деяким глобальним джерелом даних</span>\n      comments<span class=\"token operator\">:</span> DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">getComments</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">componentDidMount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Підписка на зміни</span>\n    DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">addChangeListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">componentWillUnmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Відписка</span>\n    DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">removeChangeListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">handleChange</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Оновлення стану компонента, коли у джерелі даних відбулись зміни</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      comments<span class=\"token operator\">:</span> DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">getComments</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>comments<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">comment</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Comment</span></span> <span class=\"token attr-name\">comment</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>comment<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>comment<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Пізніше, ви створите компонент <code class=\"gatsby-code-text\">BlogPost</code> для підписки на одну публікацію блогу, який реалізується за схожою логікою:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">BlogPost</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">React<span class=\"token punctuation\">.</span>Component</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>props<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">handleChange</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      blogPost<span class=\"token operator\">:</span> DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">getBlogPost</span><span class=\"token punctuation\">(</span>props<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">componentDidMount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">addChangeListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">componentWillUnmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">removeChangeListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">handleChange</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      blogPost<span class=\"token operator\">:</span> DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">getBlogPost</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>props<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">TextBlock</span></span> <span class=\"token attr-name\">text</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>blogPost<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><code class=\"gatsby-code-text\">CommentList</code> та <code class=\"gatsby-code-text\">BlogPost</code> не є ідентичними — вони викликають різні методи <code class=\"gatsby-code-text\">DataSource</code> та відображають різний інтерфейс. Однак значна частина їх реалізації збігається:</p>\n<ul>\n<li>Після монтування вони підписуються на зміни у <code class=\"gatsby-code-text\">DataSource</code>.</li>\n<li>Викликають <code class=\"gatsby-code-text\">setState</code>, коли у джерелі даних відбуваються зміни.</li>\n<li>При демонтуванні вони відписуються від <code class=\"gatsby-code-text\">DataSource</code>.</li>\n</ul>\n<p>Чи можете ви уявити, що у великому додатку ця схема підписки буде виникати знову і знову? Нам потрібна абстракція, яка дозволить описати цю логіку в одному місці та звертатися до неї у багатьох компонентах. Саме тут доречно використати компонент вищого порядку.</p>\n<p>Ми можемо написати функцію, що створює компоненти, як <code class=\"gatsby-code-text\">CommentList</code> чи <code class=\"gatsby-code-text\">BlogPost</code>, які підписуються на <code class=\"gatsby-code-text\">DataSource</code>. В якості одного з аргументів функція буде приймати дочірній компонент, який отримає дані з підписки у якості власного prop. Назвемо цю функцію <code class=\"gatsby-code-text\">withSubscription</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">const</span> CommentListWithSubscription <span class=\"token operator\">=</span> <span class=\"token function\">withSubscription</span><span class=\"token punctuation\">(</span>\n  CommentList<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">DataSource</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">getComments</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> BlogPostWithSubscription <span class=\"token operator\">=</span> <span class=\"token function\">withSubscription</span><span class=\"token punctuation\">(</span>\n  BlogPost<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">DataSource<span class=\"token punctuation\">,</span> props</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">getBlogPost</span><span class=\"token punctuation\">(</span>props<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Перший параметр це обгорнутий компонент, а другий — функція, що отримує <code class=\"gatsby-code-text\">DataSource</code> та пропси, і вилучає потрібні нам дані.</p>\n<p>Коли <code class=\"gatsby-code-text\">CommentListWithSubscription</code> і <code class=\"gatsby-code-text\">BlogPostWithSubscription</code> відображаються, у <code class=\"gatsby-code-text\">CommentList</code> і <code class=\"gatsby-code-text\">BlogPost</code> буде переданий параметр <code class=\"gatsby-code-text\">data</code> у якості пропа з актуальними даними, отриманими від <code class=\"gatsby-code-text\">DataSource</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// Ця функція отримує компонент ...</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">withSubscription</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">WrappedComponent<span class=\"token punctuation\">,</span> selectData</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ... та повертає інший компонент ...</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">extends</span> React<span class=\"token punctuation\">.</span>Component <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>props<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">handleChange</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n        data<span class=\"token operator\">:</span> <span class=\"token function\">selectData</span><span class=\"token punctuation\">(</span>DataSource<span class=\"token punctuation\">,</span> props<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">componentDidMount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// ... тут відбувається підписка ...</span>\n      DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">addChangeListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">componentWillUnmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      DataSource<span class=\"token punctuation\">.</span><span class=\"token function\">removeChangeListener</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleChange<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">handleChange</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">setState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        data<span class=\"token operator\">:</span> <span class=\"token function\">selectData</span><span class=\"token punctuation\">(</span>DataSource<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>props<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// ... та відображується обгорнутий компонент зі свіжими даними!</span>\n      <span class=\"token comment\">// Зверніть увагу, що ми передаємо усі пропси</span>\n      <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">WrappedComponent</span></span> <span class=\"token attr-name\">data</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">}</span></span> <span class=\"token spread\"><span class=\"token punctuation\">{</span><span class=\"token punctuation\">...</span><span class=\"token attr-value\">this</span><span class=\"token punctuation\">.</span><span class=\"token attr-value\">props</span><span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Зверніть увагу на те, що КВП не модифікує отриманий компонент та не наслідує його поведінку. Швидше, КВП створює <em>композицію</em>, <em>обгортаючи</em> оригінальний компонент у контейнер. КВП є чистою функцією без побічних ефектів.</p>\n<p>От і все! Обгорнутий компонент отримує всі пропси, що були передані до контейнера, разом з новим prop — <code class=\"gatsby-code-text\">data</code>, який він використовує для відображення UI. Для компонента вищого порядку не має значення, як саме дані будуть використані, а для обгорнутого компонента не має значення, звідки вони з’явились.</p>\n<p>Оскільки <code class=\"gatsby-code-text\">withSubscription</code> це звичайна функція, ви можете додати стільки аргументів, скільки забажаєте. Наприклад, ви можете зробити ім’я параметру <code class=\"gatsby-code-text\">data</code> конфігуруємим, щоб додатково ізолювати КВП від обгорнутого компонента. Або ви можете прийняти аргумент, що налаштує <code class=\"gatsby-code-text\">shouldComponentUpdate</code> чи джерело даних. Це все можливо тому, що КВП має повний контроль над тим, як компонент буде визначений.</p>\n<p>Як і між звичайними компонентами, взаємодія між <code class=\"gatsby-code-text\">withSubscription</code> і обгорнутим компонентом здійснюється лише за допомогою пропсів. Це дозволяє легко замінити один КВП на інший, якщо вони забезпечують однакові пропси для обгорнутого компонента. Це може бути корисним, наприклад, якщо ви зміните бібліотеки для отримання даних.</p>\n<h2 id=\"dont-mutate-the-original-component-use-composition\"><a href=\"#dont-mutate-the-original-component-use-composition\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Не змінюйте обгорнутий компонент. Використовуйте композицію. </h2>\n<p>Втримайтеся від бажання змінити прототип компонента (чи мутувати його) всередині КВП.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">logProps</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">InputComponent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token class-name\">InputComponent</span><span class=\"token punctuation\">.</span>prototype<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">componentDidUpdate</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevProps</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Current props: '</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>props<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Previous props: '</span><span class=\"token punctuation\">,</span> prevProps<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// Якщо ми повертаємо лише той самий отриманий компонент - це натяк, що він</span>\n  <span class=\"token comment\">// був мутований</span>\n  <span class=\"token keyword\">return</span> InputComponent<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// EnhancedComponent буде щоразу друкувати в консоль, коли отримає новий проп</span>\n<span class=\"token keyword\">const</span> EnhancedComponent <span class=\"token operator\">=</span> <span class=\"token function\">logProps</span><span class=\"token punctuation\">(</span>InputComponent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>З цим пов’язано кілька проблем. Одна полягає у тому, що <code class=\"gatsby-code-text\">InputComponent</code> не може бути використаний знову окремо від <code class=\"gatsby-code-text\">EnhancedComponent</code>. Більш важливо, якщо ви застосуєте інший КВП до <code class=\"gatsby-code-text\">EnhancedComponent</code>, який, наприклад, у свою чергу <em>також</em> мутує <code class=\"gatsby-code-text\">componentDidUpdate</code>, функціональність першого КВП буде перезаписана! Цей КВП також не буде працювати з функціональними компонентами, які не мають методів життєвого циклу.</p>\n<p>Мутуючий КВП є крихкою абстракцією — споживач повинен знати, як вони реалізуються, щоб уникнути конфліктів з іншими КВП.</p>\n<p>Замість мутації, КВП мають реалізовувати композицію, обгортаючи переданий компонент у контейнер:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">logProps</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">WrappedComponent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">extends</span> React<span class=\"token punctuation\">.</span>Component <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">componentDidUpdate</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevProps</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Current props: '</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>props<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Previous props: '</span><span class=\"token punctuation\">,</span> prevProps<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Обгорайте переданий компонент у контейнер, не мутуючи його!</span>\n      <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">WrappedComponent</span></span> <span class=\"token spread\"><span class=\"token punctuation\">{</span><span class=\"token punctuation\">...</span><span class=\"token attr-value\">this</span><span class=\"token punctuation\">.</span><span class=\"token attr-value\">props</span><span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Цей КВП має таку ж функціональність, як і мутована версія, уникаючи при цьому можливих проблем. Він однаково добре працює з класовими та функціональними компонентами. А тому, що це чиста функція, вона може бути поєднана з іншим КВП, або навіть сама з собою.</p>\n<p>Можливо, ви вже помітили схожість між КВП та патерном, що називається компонент-контейнер. Нагадаємо, що <strong>компонент-контейнер</strong> є частиною політики відокремлення відповідальності між високорівневими та низькорівневими задачами. Контейнери керують такими речами, як підписка на зовнішні ресурси та внутрішній стан, та передають пропси у компоненти, що у свою чергу відповідають за відображення UI. КВП використовують контейнери, як частину власної реалізації. Ви можете вважати КВП параметризованим визначенням компонента-контейнера.</p>\n<h2 id=\"convention-pass-unrelated-props-through-to-the-wrapped-component\"><a href=\"#convention-pass-unrelated-props-through-to-the-wrapped-component\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Конвенція: передавайте сторонні пропси обгорненому компоненту </h2>\n<p>КВП додає компоненту функціональність. Він не повинен змінювати його початкове призначення. Очікується, що повернений КВП компонент буде мати інтерфейс аналогічний обгорнутому.</p>\n<p>КВП повинні передавати ті пропси, що не пов’язані з їх функціональністю, у незмінному стані. Більшість компонентів вищого порядку мають рендер-метод, схожий на цей:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Відфільтруйте зайві пропси, що характерні для КВП та не мають</span>\n  <span class=\"token comment\">// потрапити до компонента</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> extraProp<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>passThroughProps <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>props<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Створіть ін’єкцію для обгорнутого компонента.</span>\n  <span class=\"token comment\">// Зазвичай це значення стану або методи екземпляра</span>\n  <span class=\"token keyword\">const</span> injectedProp <span class=\"token operator\">=</span> someStateOrInstanceMethod<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Передайте пропси до обгорнутого компонента</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">WrappedComponent</span></span>\n      <span class=\"token attr-name\">injectedProp</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>injectedProp<span class=\"token punctuation\">}</span></span>\n      <span class=\"token spread\"><span class=\"token punctuation\">{</span><span class=\"token punctuation\">...</span><span class=\"token attr-value\">passThroughProps</span><span class=\"token punctuation\">}</span></span>\n    <span class=\"token punctuation\">/></span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Ця угода допомагає забезпечити максимальну гнучкість та можливість повторного використання КВП.</p>\n<h2 id=\"convention-maximizing-composability\"><a href=\"#convention-maximizing-composability\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Конвенція: максимізація композиційності </h2>\n<p>Не всі КВП виглядають однаково. Іноді вони приймають лише один аргумент — компонент, що буде обгорнений:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">const</span> NavbarWithRouter <span class=\"token operator\">=</span> <span class=\"token function\">withRouter</span><span class=\"token punctuation\">(</span>Navbar<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Зазвичай КВП приймає додаткові аргументи. У цьому прикладі з Relay об’єкт конфігурації використовується для визначення залежностей даних компонента:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">const</span> CommentWithRelay <span class=\"token operator\">=</span> Relay<span class=\"token punctuation\">.</span><span class=\"token function\">createContainer</span><span class=\"token punctuation\">(</span>Comment<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Найбільш поширений спосіб виклику КВП виглядає так:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// Функція `connect` у React та Redux</span>\n<span class=\"token keyword\">const</span> ConnectedComment <span class=\"token operator\">=</span> <span class=\"token function\">connect</span><span class=\"token punctuation\">(</span>commentSelector<span class=\"token punctuation\">,</span> commentActions<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>CommentList<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><em>Що?!</em> Якщо розділити це на частини, то легше буде побачити, що відбувається.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// `connect` це функція, що повертає іншу функцію</span>\n<span class=\"token keyword\">const</span> enhance <span class=\"token operator\">=</span> <span class=\"token function\">connect</span><span class=\"token punctuation\">(</span>commentListSelector<span class=\"token punctuation\">,</span> commentListActions<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// Повернена функція є КВП, який поверне компонент підключенний до Redux стору</span>\n<span class=\"token keyword\">const</span> ConnectedComment <span class=\"token operator\">=</span> <span class=\"token function\">enhance</span><span class=\"token punctuation\">(</span>CommentList<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Іншими словами, <code class=\"gatsby-code-text\">connect</code> - це функція вищого порядку, яка повертає компонент вищого порядку!</p>\n<p>Ця форма може здатися заплутаною або непотрібною, але вона має корисну властивість. Одноаргументні КВП, як і той, який повертається функцією <code class=\"gatsby-code-text\">connect</code>, мають сигнатуру <code class=\"gatsby-code-text\">Component =&gt; Component</code>. Функції, з однаковим типом результату та єдиного аргументу, легко поєднуються у композицію.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// Замість цього ...</span>\n<span class=\"token keyword\">const</span> EnhancedComponent <span class=\"token operator\">=</span> <span class=\"token function\">withRouter</span><span class=\"token punctuation\">(</span><span class=\"token function\">connect</span><span class=\"token punctuation\">(</span>commentSelector<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>WrappedComponent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// ... ви можете використати композиційну функцію</span>\n<span class=\"token comment\">// compose(f, g, h) теж саме, що (...args) => f(g(h(...args)))</span>\n<span class=\"token keyword\">const</span> enhance <span class=\"token operator\">=</span> <span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">// Обидва параметра є КВП та приймають лише один аргумент</span>\n  withRouter<span class=\"token punctuation\">,</span>\n  <span class=\"token function\">connect</span><span class=\"token punctuation\">(</span>commentSelector<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> EnhancedComponent <span class=\"token operator\">=</span> <span class=\"token function\">enhance</span><span class=\"token punctuation\">(</span>WrappedComponent<span class=\"token punctuation\">)</span></code></pre></div>\n<p>(Саме ця властивість дозволяє використовувати <code class=\"gatsby-code-text\">connect</code> та інші поширюючи функціональність КВП у якості експериментальних JavaScript декораторів.)</p>\n<p>Ви можете знайти допоміжну функцію <code class=\"gatsby-code-text\">compose</code> у багатьох сторонніх бібліотеках, включаючи lodash (під назвою <a href=\"https://lodash.com/docs/#flowRight\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"gatsby-code-text\">lodash.flowRight</code></a>), <a href=\"https://redux.js.org/api/compose\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Redux</a> та <a href=\"https://ramdajs.com/docs/#compose\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Ramda</a>.</p>\n<h2 id=\"convention-wrap-the-display-name-for-easy-debugging\"><a href=\"#convention-wrap-the-display-name-for-easy-debugging\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Конвенція: повертайте ім’я обгорнутого компонента для легшого дебагу </h2>\n<p>Створений КВП компонент-контейнер відображається у <a href=\"https://github.com/facebook/react-devtools\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">React Developer Tools</a>, як і будь-який інший компонент. Для того, щоб полегшити процес налагодження, визначте відображуване ім’я, яке повідомляє, що це результат КВП.</p>\n<p>Найпоширеніший прийом - обгортати відображуване ім’я загорнутого компонента. Отже, якщо ваш компонент вищого порядку названий <code class=\"gatsby-code-text\">withSubscription</code>, а відображене ім’я загорнутого компонента - <code class=\"gatsby-code-text\">CommentList</code>, визначте відображуване ім’я як <code class=\"gatsby-code-text\">WithSubscription(CommentList)</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">withSubscription</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">WrappedComponent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">WithSubscription</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">React<span class=\"token punctuation\">.</span>Component</span> <span class=\"token punctuation\">{</span><span class=\"token comment\">/* ... */</span><span class=\"token punctuation\">}</span>\n  WithSubscription<span class=\"token punctuation\">.</span>displayName <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">WithSubscription(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token function\">getDisplayName</span><span class=\"token punctuation\">(</span>WrappedComponent<span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">)</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> WithSubscription<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">getDisplayName</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">WrappedComponent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> WrappedComponent<span class=\"token punctuation\">.</span>displayName <span class=\"token operator\">||</span> WrappedComponent<span class=\"token punctuation\">.</span>name <span class=\"token operator\">||</span> <span class=\"token string\">'Component'</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"caveats\"><a href=\"#caveats\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Застереження </h2>\n<p>Якщо ви лише починаєте використовувати React, то варто зазначити, що компоненти вищого порядку можуть стати причиною неочевидних проблем.</p>\n<h3 id=\"dont-use-hocs-inside-the-render-method\"><a href=\"#dont-use-hocs-inside-the-render-method\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Не використовуйте КВП у середині рендер-методів </h3>\n<p>Алгоритм порівняння React, що відомий як reconciliation (або узгодження), використовує перевірку на тотожність компонента, щоб визначити, чи слід оновити існуюче піддерево компонентів або слід знищити його та змонтувати нове. Якщо компонент, що був повернений рендер-методом, ідентичний (<code class=\"gatsby-code-text\">===</code>) попередньому результату, React рекурсивно оновлює піддерева, порівнюючи його з новим. Якщо вони не тотожні, то попереднє піддерево буде повністю розмонтовано.</p>\n<p>Зазвичай вам не потрібно думати про це. Однак, це важливо для компонента вищого порядку, оскільки це означає, що ви не можете застосувати КВП до компонента в рендер-методі іншого компонента:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Під час кожного виклику рендер-методу створюється новий екземпляр EnhancedComponent</span>\n  <span class=\"token comment\">// EnhancedComponent1 !== EnhancedComponent2</span>\n  <span class=\"token keyword\">const</span> EnhancedComponent <span class=\"token operator\">=</span> <span class=\"token function\">enhance</span><span class=\"token punctuation\">(</span>MyComponent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// Це призводить до того, що все піддерево щоразу монтується/демонтується!</span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">EnhancedComponent</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Проблема тут полягає не лише в продуктивності — повторне перерахування компонента втрачає стан цього компонента та всіх його потомків.</p>\n<p>Замість цього застосовуйте КВП за межами визначення компонента, щоб отриманий компонент був створений лише один раз. У цьому випадку React буде порівнювати один і той же компонент при повторному виклику рендер-метода.</p>\n<p>У тих рідкісних випадках, коли вам потрібно динамічно застосовувати КВП, ви можете зробити це в методах життєвого циклу компонента або в його конструкторі.</p>\n<h3 id=\"static-methods-must-be-copied-over\"><a href=\"#static-methods-must-be-copied-over\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Копіюйте статичні методи </h3>\n<p>Іноді корисно визначити статичні методи компонента. Наприклад, статичний метод <code class=\"gatsby-code-text\">getFragment</code> у бібліотеці Relay дає можливість визначити композицію з фрагментів даних GraphQL.</p>\n<p>Якщо ви застосовуєте КВП, то обгортаєте оригінальний компонент контейнером. Це означає, що новий компонент не матиме статичних методів оригінального компонента.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// Визначте статичний метод</span>\nWrappedComponent<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">staticMethod</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token comment\">/*...*/</span><span class=\"token punctuation\">}</span>\n<span class=\"token comment\">// Тепер застосуйте КВП</span>\n<span class=\"token keyword\">const</span> EnhancedComponent <span class=\"token operator\">=</span> <span class=\"token function\">enhance</span><span class=\"token punctuation\">(</span>WrappedComponent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// EnhancedComponent не має статичного методу</span>\n<span class=\"token keyword\">typeof</span> EnhancedComponent<span class=\"token punctuation\">.</span>staticMethod <span class=\"token operator\">===</span> <span class=\"token string\">'undefined'</span> <span class=\"token comment\">// true</span></code></pre></div>\n<p>Вам необхідно скопіювати методи до того, як повернути новий компонент, щоб вирішити цю проблему:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">enhance</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">WrappedComponent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">Enhance</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">React<span class=\"token punctuation\">.</span>Component</span> <span class=\"token punctuation\">{</span><span class=\"token comment\">/*...*/</span><span class=\"token punctuation\">}</span>\n  <span class=\"token comment\">// Потрібно точно знати, який метод(и) скопіювати :(</span>\n  Enhance<span class=\"token punctuation\">.</span>staticMethod <span class=\"token operator\">=</span> WrappedComponent<span class=\"token punctuation\">.</span>staticMethod<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> Enhance<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>На жаль для цього необхідно точно знати, які методи потрібно скопіювати. Для автоматичного копіювання всіх статичних методів ви можете використати <a href=\"https://github.com/mridgway/hoist-non-react-statics\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"gatsby-code-text\">hoist-non-react-statics</code></a>:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">import</span> hoistNonReactStatic <span class=\"token keyword\">from</span> <span class=\"token string\">'hoist-non-react-statics'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">enhance</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">WrappedComponent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">Enhance</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">React<span class=\"token punctuation\">.</span>Component</span> <span class=\"token punctuation\">{</span><span class=\"token comment\">/*...*/</span><span class=\"token punctuation\">}</span>\n  <span class=\"token function\">hoistNonReactStatic</span><span class=\"token punctuation\">(</span>Enhance<span class=\"token punctuation\">,</span> WrappedComponent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> Enhance<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Ще одне можливе рішення — експортувати статичні методи окремо від компонента.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// Замість цього ...</span>\nMyComponent<span class=\"token punctuation\">.</span>someFunction <span class=\"token operator\">=</span> someFunction<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> MyComponent<span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// ... експортуйте метод окремо ...</span>\n<span class=\"token keyword\">export</span> <span class=\"token punctuation\">{</span> someFunction <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// ... в модулі-споживачі ми можемо імпортувати обидва</span>\n<span class=\"token keyword\">import</span> MyComponent<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> someFunction <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'./MyComponent.js'</span><span class=\"token punctuation\">;</span></code></pre></div>\n<h3 id=\"refs-arent-passed-through\"><a href=\"#refs-arent-passed-through\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Рефи не передаються </h3>\n<p>Попри те, що попередня конвенція наполягає на тому, щоб передавати всі пропси до обгорнутого компонента, це не стосується рефів. Річ у тому, що <code class=\"gatsby-code-text\">ref</code> насправді не є пропом, як, наприклад, <code class=\"gatsby-code-text\">key</code>, а тому інакше оброблюється React. Якщо ви додасте <code class=\"gatsby-code-text\">ref</code> до компонента, що був повернений КВП, він буде вказувати на екземпляр зовнішнього контейнера, а не на обгорнутий компонент.</p>\n<p>Розв’язання цієї проблеми є використання API <code class=\"gatsby-code-text\">React.forwardRef</code> (введений з React 16.3). <a href=\"/docs/forwarding-refs.html\">Дізнайтеся більше про це в розділі Переадресація рефів</a>.</p>","frontmatter":{"title":"Компоненти вищого порядку","next":null,"prev":null},"fields":{"path":"content/docs/higher-order-components.md","slug":"docs/higher-order-components.html"}}},"pageContext":{"slug":"docs/higher-order-components.html"}},"staticQueryHashes":[]}