{"componentChunkName":"component---src-templates-docs-js","path":"/docs/higher-order-components.html","result":{"data":{"markdownRemark":{"html":"<p>Компонент высшего порядка (Higher-Order Component, HOC) — это один из продвинутых способов для повторного использования логики. HOC не являются частью API React, но часто применяются из-за композиционной природы компонентов.</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>HOC часто встречаются в сторонних библиотеках, например <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=\"https://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>HOC для сквозной функциональности </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>Традиционные компоненты подразумевают многократное использование, но не позволяют с лёгкостью решить некоторые проблемы.</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>Теперь мы решили реализовать новый компонент, который отслеживает изменения конкретной публикации и повторяет уже знакомый нам шаблон:</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\">DataSource</code>.</li>\n<li>Оба отписываются от <code class=\"gatsby-code-text\">DataSource</code> при размонтировании.</li>\n</ul>\n<p>Можете представить, что в больших приложениях связка «подписаться на <code class=\"gatsby-code-text\">DataSource</code>, затем вызвать <code class=\"gatsby-code-text\">setState</code>» повторяется очень часто. Было бы здорово абстрагировать эту функциональность и использовать её в других компонентах. </p>\n<p>Давайте реализуем функцию <code class=\"gatsby-code-text\">withSubscription</code> — она будет создавать компоненты и подписывать их на обновления <code class=\"gatsby-code-text\">DataSource</code> (наподобие <code class=\"gatsby-code-text\">CommentList</code> и <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\">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\">DataSource</code> через проп <code class=\"gatsby-code-text\">data</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>Заметьте, что HOC ничего не меняет и не наследует поведение оборачиваемого компонента, вместо этого HOC <em>оборачивает</em> оригинальный компонент в контейнер посредством <em>композиции</em>. HOC является чистой функцией без побочных эффектов.</p>\n<p>Вот и всё! Оборачиваемый компонент получает все пропсы, переданные контейнеру, а также проп <code class=\"gatsby-code-text\">data</code>. Для HOC не важно, как будут использоваться данные, а оборачиваемому компоненту не важно, откуда они берутся.</p>\n<p>Так как <code class=\"gatsby-code-text\">withSubscription</code> — это обычная функция, то мы можем убрать или добавить любое количество аргументов. Например, мы могли бы сделать конфигурируемым название пропа <code class=\"gatsby-code-text\">data</code> и ещё больше изолировать HOC от оборачиваемого компонента. Также мы можем добавить аргумент для конфигурации <code class=\"gatsby-code-text\">shouldComponentUpdate</code> или источника данных. Всё это возможно, потому что HOC полностью контролирует процесс создания компонента.</p>\n<p>Взаимодействие между <code class=\"gatsby-code-text\">withSubscription</code> и оборачиваемым компонентом осуществляется с помощью пропсов, так же, как и между обычными компонентами. Благодаря этому мы можем с лёгкостью заменить один HOC на другой, при условии, что они передают одни и те же пропсы в оборачиваемый компонент. Это может пригодиться если, например, мы решим поменять библиотеку получения данных.</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>Не поддавайтесь соблазну менять прототип компонента (или мутировать его любым другим способом) внутри HOC.</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\">'Текущие пропсы: '</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\">'Предыдущие пропсы: '</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 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> в другой HOC, который <em>тоже</em> меняет <code class=\"gatsby-code-text\">componentDidUpdate</code>, то мы сотрём функциональность заданную первым HOC! Более того, <code class=\"gatsby-code-text\">EnhancedComponent</code> не работает с функциональными компонентами, потому что у них отсутствуют методы жизненного цикла.</p>\n<p>Мутирующие HOC являются хрупкой абстракцией, они конфликтуют с другими HOC, мы не сможем просто применять их без того, чтобы знать что именно они меняют.</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\">'Текущие пропсы: '</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\">'Предыдущие пропсы: '</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>Этот HOC обладает такой же функциональностью, как и предыдущий, но не создаёт конфликтов с другими HOC и работает как с функциональными, так и с классовыми компонентами. Более того, HOC реализован с помощью чистой функции, поэтому его можно совмещать с другими HOC, или даже самого с собой.</p>\n<p>Возможно, вы уже заметили сходство между HOC и <strong>компонентами-контейнерами</strong>. Напомним, что при помощи контейнеров мы обычно разделяем общую функциональность от частной. Например, в контейнере мы будем управлять внутренним состоянием или подпиской на внешние ресурсы, и через пропсы передавать данные в компоненты, ответственные за рендер UI. При реализации HOC мы тоже используем контейнеры. Можно сказать что HOC — это инструмент для параметризированного создания контейнеров.</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>HOC добавляют компонентам функциональность, но они не должны менять их оригинальное предназначение. Ожидается, что интерфейс компонента, который вы возвращаете из HOC, будет похож на интерфейс оборачиваемого компонента.</p>\n<p>Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться без изменений оборачиваемому компоненту. Рендер-метод большинства HOC похож на следующий:</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\">// Отфильтруйте пропсы применимые только к этому HOC и которые не нужно передавать дальше</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 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>Не все HOC выглядят одинаково. Некоторые принимают всего лишь один аргумент — оборачиваемый компонент:</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>Обычно HOC принимают несколько аргументов. В данном примере из 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>Самый распространённый способ вызова HOC выглядит так:</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\n<span class=\"token comment\">// Эта функция и есть HOC. Она возвращает компонент, подключённый к хранилищу 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> возвращает HOC с подписью <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\">// Оба параметра являются HOC и принимают один единственный аргумент</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> и другие расширяющие функциональность HOC в качестве экспериментальных 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>Созданные HOC компоненты-контейнеры отображаются в консоли <a href=\"https://github.com/facebook/react-devtools\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">инструментов разработки React</a> наряду с другими компонентами. Для более лёгкой отладки вы можете задать имя, которое подскажет, что определённый компонент был создан с помощью HOC.</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>Вы можете столкнуться с неочевидными проблемами, когда работаете с компонентами высшего порядка.</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>Не используйте HOC внутри рендер-метода </h3>\n<p>Алгоритм сравнения React (известный как согласование или reconciliation) использует тождественность компонентов чтобы определить нужно ли обновить существующее поддерево, или убрать и монтировать вместо него новое. Если компонент, полученный из <code class=\"gatsby-code-text\">render</code>, идентичен (<code class=\"gatsby-code-text\">===</code>) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать поддерево. Если компоненты не равны, 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>Не применяйте HOC в определении другого компонента. Сначала нужно отдельно получить компонент из HOC, и только потом использовать его. Таким образом React будет сравнивать один и тот же компонент при повторном рендере.</p>\n<p>При необходимости (в редких случаях) можно динамически применять HOC в методах жизненного цикла или конструкторе компонента.</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>Когда мы применяем HOC, то заворачиваем оригинальный компонент в контейнер. Поэтому у нового компонента не будет статических методов оригинального компонента.</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\">// Теперь применим HOC</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\">// У расширенного компонента нет статических методов</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\">hoist-non-react-statics</a>, чтобы автоматически скопировать не связанные с React статические методы:</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. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерархии контейнера, а не на оборачиваемый компонент.</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":[]}