Files
react/docs/higher-order-components.html
T
Dan Abramov 7bc32d9148 Rebuild
2017-06-26 18:29:28 +01:00

751 lines
52 KiB
HTML

<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Higher-Order Components - React</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:title" content="Higher-Order Components - React">
<meta property="og:type" content="website">
<meta property="og:url" content="https://facebook.github.io/react/docs/higher-order-components.html">
<meta property="og:image" content="https://facebook.github.io/react/img/logo_og.png">
<meta property="og:description" content="A JavaScript library for building user interfaces">
<meta property="fb:app_id" content="623268441017527">
<link rel="shortcut icon" href="/react/favicon.ico">
<link rel="alternate" type="application/rss+xml" title="React" href="https://facebook.github.io/react/feed.xml">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" />
<link rel="stylesheet" href="/react/css/syntax.css">
<link rel="stylesheet" href="/react/css/codemirror.css">
<link rel="stylesheet" href="/react/css/react.css">
<script src="//use.typekit.net/vqa1hcx.js"></script>
<script>try{Typekit.load();}catch(e){}</script>
<!--[if lte IE 8]>
<script src="https://unpkg.com/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
<script src="https://unpkg.com/es5-shim@4.5.9/es5-shim.min.js"></script>
<script src="https://unpkg.com/es5-shim@4.5.9/es5-sham.min.js"></script>
<![endif]-->
<script src="https://unpkg.com/docsearch.js@1.5.0/dist/cdn/docsearch.min.js"></script>
<script src="https://unpkg.com/codemirror@5.15.2"></script>
<script src="https://unpkg.com/codemirror@5.15.2/mode/javascript/javascript.js"></script>
<script src="https://unpkg.com/codemirror@5.15.2/mode/xml/xml.js"></script>
<script src="https://unpkg.com/codemirror@5.15.2/mode/jsx/jsx.js"></script>
<script src="https://unpkg.com/react/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom/dist/react-dom.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="/react/js/live_editor.js"></script>
</head>
<body>
<div class="container">
<div class="nav-main">
<div class="wrap">
<a class="nav-home" href="/react/">
<img class="nav-logo" src="/react/img/logo.svg" width="36" height="36">
React
</a>
<div class="nav-lists">
<ul class="nav-site nav-site-internal">
<li><a href="/react/docs/hello-world.html" class="active">Docs</a></li>
<li><a href="/react/tutorial/tutorial.html">Tutorial</a></li>
<li><a href="/react/community/support.html">Community</a></li>
<li><a href="/react/blog/">Blog</a></li>
<li class="nav-site-search">
<input id="algolia-doc-search" type="text" placeholder="Search docs..." />
</li>
</ul>
<ul class="nav-site nav-site-external">
<li><a href="https://github.com/facebook/react">GitHub</a></li>
<li><a href="https://github.com/facebook/react/releases">v15.6.1</a></li>
</ul>
</div>
</div>
</div>
<section class="content wrap documentationContent">
<div class="inner-content">
<a class="edit-page-link" href="https://github.com/facebook/react/tree/master/docs/docs/higher-order-components.md" target="_blank">Edit on GitHub</a>
<h1>
Higher-Order Components
</h1>
<div class="subHeader"></div>
<p>A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React&#39;s compositional nature.</p>
<p>Concretely, <strong>a higher-order component is a function that takes a component and returns a new component.</strong></p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">const</span> <span class="nx">EnhancedComponent</span> <span class="o">=</span> <span class="nx">higherOrderComponent</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">);</span>
</code></pre></div>
<p>Whereas a component transforms props into UI, a higher-order component transforms a component into another component.</p>
<p>HOCs are common in third-party React libraries, such as Redux&#39;s <a href="https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options"><code>connect</code></a> and Relay&#39;s <a href="https://facebook.github.io/relay/docs/api-reference-relay.html#createcontainer-static-method"><code>createContainer</code></a>.</p>
<p>In this document, we&#39;ll discuss why higher-order components are useful, and how to write your own.</p>
<h2>Use HOCs For Cross-Cutting Concerns</h2>
<blockquote>
<p><strong>Note</strong></p>
<p>We previously recommended mixins as a way to handle cross-cutting concerns. We&#39;ve since realized that mixins create more trouble than they are worth. <a href="/react/blog/2016/07/13/mixins-considered-harmful.html">Read more</a> about why we&#39;ve moved away from mixins and how you can transition your existing components.</p>
</blockquote>
<p>Components are the primary unit of code reuse in React. However, you&#39;ll find that some patterns aren&#39;t a straightforward fit for traditional components.</p>
<p>For example, say you have a <code>CommentList</code> component that subscribes to an external data source to render a list of comments:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">CommentList</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="kr">super</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
<span class="c1">// &quot;DataSource&quot; is some global data source</span>
<span class="nx">comments</span><span class="o">:</span> <span class="nx">DataSource</span><span class="p">.</span><span class="nx">getComments</span><span class="p">()</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Subscribe to changes</span>
<span class="nx">DataSource</span><span class="p">.</span><span class="nx">addChangeListener</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">componentWillUnmount</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Clean up listener</span>
<span class="nx">DataSource</span><span class="p">.</span><span class="nx">removeChangeListener</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">handleChange</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Update component state whenever the data source changes</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
<span class="nx">comments</span><span class="o">:</span> <span class="nx">DataSource</span><span class="p">.</span><span class="nx">getComments</span><span class="p">()</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
<span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">comments</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">comment</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
<span class="o">&lt;</span><span class="nx">Comment</span> <span class="nx">comment</span><span class="o">=</span><span class="p">{</span><span class="nx">comment</span><span class="p">}</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">comment</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span> <span class="o">/&gt;</span>
<span class="p">))}</span>
<span class="o">&lt;</span><span class="err">/div&gt;</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Later, you write a component for subscribing to a single blog post, which follows a similar pattern:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">BlogPost</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">blogPost</span><span class="o">:</span> <span class="nx">DataSource</span><span class="p">.</span><span class="nx">getBlogPost</span><span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">id</span><span class="p">)</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">DataSource</span><span class="p">.</span><span class="nx">addChangeListener</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">componentWillUnmount</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">DataSource</span><span class="p">.</span><span class="nx">removeChangeListener</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">handleChange</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
<span class="nx">blogPost</span><span class="o">:</span> <span class="nx">DataSource</span><span class="p">.</span><span class="nx">getBlogPost</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">id</span><span class="p">)</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">TextBlock</span> <span class="nx">text</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">blogPost</span><span class="p">}</span> <span class="o">/&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p><code>CommentList</code> and <code>BlogPost</code> aren&#39;t identical — they call different methods on <code>DataSource</code>, and they render different output. But much of their implementation is the same:</p>
<ul>
<li>On mount, add a change listener to <code>DataSource</code>.</li>
<li>Inside the listener, call <code>setState</code> whenever the data source changes.</li>
<li>On unmount, remove the change listener.</li>
</ul>
<p>You can imagine that in a large app, this same pattern of subscribing to <code>DataSource</code> and calling <code>setState</code> will occur over and over again. We want an abstraction that allows us to define this logic in a single place and share them across many components. This is where higher-order components excel.</p>
<p>We can write a function that creates components, like <code>CommentList</code> and <code>BlogPost</code>, that subscribe to <code>DataSource</code>. The function will accept as one of its arguments a child component that receives the subscribed data as a prop. Let&#39;s call the function <code>withSubscription</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">const</span> <span class="nx">CommentListWithSubscription</span> <span class="o">=</span> <span class="nx">withSubscription</span><span class="p">(</span>
<span class="nx">CommentList</span><span class="p">,</span>
<span class="p">(</span><span class="nx">DataSource</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">DataSource</span><span class="p">.</span><span class="nx">getComments</span><span class="p">()</span>
<span class="p">);</span>
<span class="kr">const</span> <span class="nx">BlogPostWithSubscription</span> <span class="o">=</span> <span class="nx">withSubscription</span><span class="p">(</span>
<span class="nx">BlogPost</span><span class="p">,</span>
<span class="p">(</span><span class="nx">DataSource</span><span class="p">,</span> <span class="nx">props</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">DataSource</span><span class="p">.</span><span class="nx">getBlogPost</span><span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">id</span><span class="p">)</span>
<span class="p">);</span>
</code></pre></div>
<p>The first parameter is the wrapped component. The second parameter retrieves the data we&#39;re interested in, given a <code>DataSource</code> and the current props.</p>
<p>When <code>CommentListWithSubscription</code> and <code>BlogPostWithSubscription</code> are rendered, <code>CommentList</code> and <code>BlogPost</code> will be passed a <code>data</code> prop with the most current data retrieved from <code>DataSource</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// This function takes a component...</span>
<span class="kd">function</span> <span class="nx">withSubscription</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">,</span> <span class="nx">selectData</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// ...and returns another component...</span>
<span class="k">return</span> <span class="kr">class</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">data</span><span class="o">:</span> <span class="nx">selectData</span><span class="p">(</span><span class="nx">DataSource</span><span class="p">,</span> <span class="nx">props</span><span class="p">)</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// ... that takes care of the subscription...</span>
<span class="nx">DataSource</span><span class="p">.</span><span class="nx">addChangeListener</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">componentWillUnmount</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">DataSource</span><span class="p">.</span><span class="nx">removeChangeListener</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">handleChange</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span>
<span class="nx">data</span><span class="o">:</span> <span class="nx">selectData</span><span class="p">(</span><span class="nx">DataSource</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">)</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// ... and renders the wrapped component with the fresh data!</span>
<span class="c1">// Notice that we pass through any additional props</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">WrappedComponent</span> <span class="nx">data</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">data</span><span class="p">}</span> <span class="p">{...</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">}</span> <span class="o">/&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span>
</code></pre></div>
<p>Note that an HOC doesn&#39;t modify the input component, nor does it use inheritance to copy its behavior. Rather, an HOC <em>composes</em> the original component by <em>wrapping</em> it in a container component. An HOC is a pure function with zero side-effects.</p>
<p>And that&#39;s it! The wrapped component receives all the props of the container, along with a new prop, <code>data</code>, which it uses to render its output. The HOC isn&#39;t concerned with how or why the data is used, and the wrapped component isn&#39;t concerned with where the data came from.</p>
<p>Because <code>withSubscription</code> is a normal function, you can add as many or as few arguments as you like. For example, you may want to make the name of the <code>data</code> prop configurable, to further isolate the HOC from the wrapped component. Or you could accept an argument that configures <code>shouldComponentUpdate</code>, or one that configures the data source. These are all possible because the HOC has full control over how the component is defined.</p>
<p>Like components, the contract between <code>withSubscription</code> and the wrapped component is entirely props-based. This makes it easy to swap one HOC for a different one, as long as they provide the same props to the wrapped component. This may be useful if you change data-fetching libraries, for example.</p>
<h2>Don&#39;t Mutate the Original Component. Use Composition.</h2>
<p>Resist the temptation to modify a component&#39;s prototype (or otherwise mutate it) inside an HOC.</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">logProps</span><span class="p">(</span><span class="nx">InputComponent</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">InputComponent</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">componentWillReceiveProps</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Current props: &#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Next props: &#39;</span><span class="p">,</span> <span class="nx">nextProps</span><span class="p">);</span>
<span class="p">};</span>
<span class="c1">// The fact that we&#39;re returning the original input is a hint that it has</span>
<span class="c1">// been mutated.</span>
<span class="k">return</span> <span class="nx">InputComponent</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// EnhancedComponent will log whenever props are received</span>
<span class="kr">const</span> <span class="nx">EnhancedComponent</span> <span class="o">=</span> <span class="nx">logProps</span><span class="p">(</span><span class="nx">InputComponent</span><span class="p">);</span>
</code></pre></div>
<p>There are a few problems with this. One is that the input component cannot be reused separately from the enhanced component. More crucially, if you apply another HOC to <code>EnhancedComponent</code> that <em>also</em> mutates <code>componentWillReceiveProps</code>, the first HOC&#39;s functionality will be overridden! This HOC also won&#39;t work with function components, which do not have lifecycle methods.</p>
<p>Mutating HOCs are a leaky abstraction—the consumer must know how they are implemented in order to avoid conflicts with other HOCs.</p>
<p>Instead of mutation, HOCs should use composition, by wrapping the input component in a container component:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">logProps</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kr">class</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">componentWillReceiveProps</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Current props: &#39;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Next props: &#39;</span><span class="p">,</span> <span class="nx">nextProps</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Wraps the input component in a container, without mutating it. Good!</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">WrappedComponent</span> <span class="p">{...</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">}</span> <span class="o">/&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>This HOC has the same functionality as the mutating version while avoiding the potential for clashes. It works equally well with class and functional components. And because it&#39;s a pure function, it&#39;s composable with other HOCs, or even with itself.</p>
<p>You may have noticed similarities between HOCs and a pattern called <strong>container components</strong>. Container components are part of a strategy of separating responsibility between high-level and low-level concerns. Containers manage things like subscriptions and state, and pass props to components that handle things like rendering UI. HOCs use containers as part of their implementation. You can think of HOCs as parameterized container component definitions.</p>
<h2>Convention: Pass Unrelated Props Through to the Wrapped Component</h2>
<p>HOCs add features to a component. They shouldn&#39;t drastically alter its contract. It&#39;s expected that the component returned from an HOC has a similar interface to the wrapped component.</p>
<p>HOCs should pass through props that are unrelated to its specific concern. Most HOCs contain a render method that looks something like this:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Filter out extra props that are specific to this HOC and shouldn&#39;t be</span>
<span class="c1">// passed through</span>
<span class="kr">const</span> <span class="p">{</span> <span class="nx">extraProp</span><span class="p">,</span> <span class="p">...</span><span class="nx">passThroughProps</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="c1">// Inject props into the wrapped component. These are usually state values or</span>
<span class="c1">// instance methods.</span>
<span class="kr">const</span> <span class="nx">injectedProp</span> <span class="o">=</span> <span class="nx">someStateOrInstanceMethod</span><span class="p">;</span>
<span class="c1">// Pass props to wrapped component</span>
<span class="k">return</span> <span class="p">(</span>
<span class="o">&lt;</span><span class="nx">WrappedComponent</span>
<span class="nx">injectedProp</span><span class="o">=</span><span class="p">{</span><span class="nx">injectedProp</span><span class="p">}</span>
<span class="p">{...</span><span class="nx">passThroughProps</span><span class="p">}</span>
<span class="o">/&gt;</span>
<span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>This convention helps ensure that HOCs are as flexible and reusable as possible.</p>
<h2>Convention: Maximizing Composability</h2>
<p>Not all HOCs look the same. Sometimes they accept only a single argument, the wrapped component:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">const</span> <span class="nx">NavbarWithRouter</span> <span class="o">=</span> <span class="nx">withRouter</span><span class="p">(</span><span class="nx">Navbar</span><span class="p">);</span>
</code></pre></div>
<p>Usually, HOCs accept additional arguments. In this example from Relay, a config object is used to specify a component&#39;s data dependencies:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">const</span> <span class="nx">CommentWithRelay</span> <span class="o">=</span> <span class="nx">Relay</span><span class="p">.</span><span class="nx">createContainer</span><span class="p">(</span><span class="nx">Comment</span><span class="p">,</span> <span class="nx">config</span><span class="p">);</span>
</code></pre></div>
<p>The most common signature for HOCs looks like this:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// React Redux&#39;s `connect`</span>
<span class="kr">const</span> <span class="nx">ConnectedComment</span> <span class="o">=</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">commentSelector</span><span class="p">,</span> <span class="nx">commentActions</span><span class="p">)(</span><span class="nx">Comment</span><span class="p">);</span>
</code></pre></div>
<p><em>What?!</em> If you break it apart, it&#39;s easier to see what&#39;s going on.</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// connect is a function that returns another function</span>
<span class="kr">const</span> <span class="nx">enhance</span> <span class="o">=</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">commentListSelector</span><span class="p">,</span> <span class="nx">commentListActions</span><span class="p">);</span>
<span class="c1">// The returned function is an HOC, which returns a component that is connected</span>
<span class="c1">// to the Redux store</span>
<span class="kr">const</span> <span class="nx">ConnectedComment</span> <span class="o">=</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">CommentList</span><span class="p">);</span>
</code></pre></div>
<p>In other words, <code>connect</code> is a higher-order function that returns a higher-order component!</p>
<p>This form may seem confusing or unnecessary, but it has a useful property. Single-argument HOCs like the one returned by the <code>connect</code> function have the signature <code>Component =&gt; Component</code>. Functions whose output type is the same as its input type are really easy to compose together.</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// Instead of doing this...</span>
<span class="kr">const</span> <span class="nx">EnhancedComponent</span> <span class="o">=</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">commentSelector</span><span class="p">)(</span><span class="nx">withRouter</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">))</span>
<span class="c1">// ... you can use a function composition utility</span>
<span class="c1">// compose(f, g, h) is the same as (...args) =&gt; f(g(h(...args)))</span>
<span class="kr">const</span> <span class="nx">enhance</span> <span class="o">=</span> <span class="nx">compose</span><span class="p">(</span>
<span class="c1">// These are both single-argument HOCs</span>
<span class="nx">connect</span><span class="p">(</span><span class="nx">commentSelector</span><span class="p">),</span>
<span class="nx">withRouter</span>
<span class="p">)</span>
<span class="kr">const</span> <span class="nx">EnhancedComponent</span> <span class="o">=</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)</span>
</code></pre></div>
<p>(This same property also allows <code>connect</code> and other enhancer-style HOCs to be used as decorators, an experimental JavaScript proposal.)</p>
<p>The <code>compose</code> utility function is provided by many third-party libraries including lodash (as <a href="https://lodash.com/docs/#flowRight"><code>lodash.flowRight</code></a>), <a href="http://redux.js.org/docs/api/compose.html">Redux</a>, and <a href="http://ramdajs.com/docs/#compose">Ramda</a>.</p>
<h2>Convention: Wrap the Display Name for Easy Debugging</h2>
<p>The container components created by HOCs show up in the <a href="https://github.com/facebook/react-devtools">React Developer Tools</a> like any other component. To ease debugging, choose a display name that communicates that it&#39;s the result of an HOC.</p>
<p>The most common technique is to wrap the display name of the wrapped component. So if your higher-order component is named <code>withSubscription</code>, and the wrapped component&#39;s display name is <code>CommentList</code>, use the display name <code>WithSubscription(CommentList)</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">withSubscription</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">class</span> <span class="nx">WithSubscription</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span><span class="cm">/* ... */</span><span class="p">}</span>
<span class="nx">WithSubscription</span><span class="p">.</span><span class="nx">displayName</span> <span class="o">=</span> <span class="err">`</span><span class="nx">WithSubscription</span><span class="p">(</span><span class="nx">$</span><span class="p">{</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)})</span><span class="err">`</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">WithSubscription</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">WrappedComponent</span><span class="p">.</span><span class="nx">displayName</span> <span class="o">||</span> <span class="nx">WrappedComponent</span><span class="p">.</span><span class="nx">name</span> <span class="o">||</span> <span class="s1">&#39;Component&#39;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<h2>Caveats</h2>
<p>Higher-order components come with a few caveats that aren&#39;t immediately obvious if you&#39;re new to React.</p>
<h3>Don&#39;t Use HOCs Inside the render Method</h3>
<p>React&#39;s diffing algorithm (called reconciliation) uses component identity to determine whether it should update the existing subtree or throw it away and mount a new one. If the component returned from <code>render</code> is identical (<code>===</code>) to the component from the previous render, React recursively updates the subtree by diffing it with the new one. If they&#39;re not equal, the previous subtree is unmounted completely.</p>
<p>Normally, you shouldn&#39;t need to think about this. But it matters for HOCs because it means you can&#39;t apply an HOC to a component within the render method of a component:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// A new version of EnhancedComponent is created on every render</span>
<span class="c1">// EnhancedComponent1 !== EnhancedComponent2</span>
<span class="kr">const</span> <span class="nx">EnhancedComponent</span> <span class="o">=</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">MyComponent</span><span class="p">);</span>
<span class="c1">// That causes the entire subtree to unmount/remount each time!</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">EnhancedComponent</span> <span class="o">/&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>The problem here isn&#39;t just about performance — remounting a component causes the state of that component and all of its children to be lost.</p>
<p>Instead, apply HOCs outside the component definition so that the resulting component is created only once. Then, its identity will be consistent across renders. This is usually what you want, anyway.</p>
<p>In those rare cases where you need to apply an HOC dynamically, you can also do it inside a component&#39;s lifecycle methods or its constructor.</p>
<h3>Static Methods Must Be Copied Over</h3>
<p>Sometimes it&#39;s useful to define a static method on a React component. For example, Relay containers expose a static method <code>getFragment</code> to facilitate the composition of GraphQL fragments.</p>
<p>When you apply an HOC to a component, though, the original component is wrapped with a container component. That means the new component does not have any of the static methods of the original component.</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// Define a static method</span>
<span class="nx">WrappedComponent</span><span class="p">.</span><span class="nx">staticMethod</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><span class="cm">/*...*/</span><span class="p">}</span>
<span class="c1">// Now apply an HOC</span>
<span class="kr">const</span> <span class="nx">EnhancedComponent</span> <span class="o">=</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">);</span>
<span class="c1">// The enhanced component has no static method</span>
<span class="k">typeof</span> <span class="nx">EnhancedComponent</span><span class="p">.</span><span class="nx">staticMethod</span> <span class="o">===</span> <span class="s1">&#39;undefined&#39;</span> <span class="c1">// true</span>
</code></pre></div>
<p>To solve this, you could copy the methods onto the container before returning it:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">class</span> <span class="nx">Enhance</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span><span class="cm">/*...*/</span><span class="p">}</span>
<span class="c1">// Must know exactly which method(s) to copy :(</span>
<span class="nx">Enhance</span><span class="p">.</span><span class="nx">staticMethod</span> <span class="o">=</span> <span class="nx">WrappedComponent</span><span class="p">.</span><span class="nx">staticMethod</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">Enhance</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>However, this requires you to know exactly which methods need to be copied. You can use <a href="https://github.com/mridgway/hoist-non-react-statics">hoist-non-react-statics</a> to automatically copy all non-React static methods:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">import</span> <span class="nx">hoistNonReactStatic</span> <span class="nx">from</span> <span class="s1">&#39;hoist-non-react-statics&#39;</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">WrappedComponent</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">class</span> <span class="nx">Enhance</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span><span class="cm">/*...*/</span><span class="p">}</span>
<span class="nx">hoistNonReactStatic</span><span class="p">(</span><span class="nx">Enhance</span><span class="p">,</span> <span class="nx">WrappedComponent</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">Enhance</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Another possible solution is to export the static method separately from the component itself.</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// Instead of...</span>
<span class="nx">MyComponent</span><span class="p">.</span><span class="nx">someFunction</span> <span class="o">=</span> <span class="nx">someFunction</span><span class="p">;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="nx">MyComponent</span><span class="p">;</span>
<span class="c1">// ...export the method separately...</span>
<span class="kr">export</span> <span class="p">{</span> <span class="nx">someFunction</span> <span class="p">};</span>
<span class="c1">// ...and in the consuming module, import both</span>
<span class="kr">import</span> <span class="nx">MyComponent</span><span class="p">,</span> <span class="p">{</span> <span class="nx">someFunction</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./MyComponent.js&#39;</span><span class="p">;</span>
</code></pre></div>
<h3>Refs Aren&#39;t Passed Through</h3>
<p>While the convention for higher-order components is to pass through all props to the wrapped component, it&#39;s not possible to pass through refs. That&#39;s because <code>ref</code> is not really a prop — like <code>key</code>, it&#39;s handled specially by React. If you add a ref to an element whose component is the result of an HOC, the ref refers to an instance of the outermost container component, not the wrapped component.</p>
<p>If you find yourself facing this problem, the ideal solution is to figure out how to avoid using <code>ref</code> at all. Occasionally, users who are new to the React paradigm rely on refs in situations where a prop would work better.</p>
<p>That said, there are times when refs are a necessary escape hatch — React wouldn&#39;t support them otherwise. Focusing an input field is an example where you may want imperative control of a component. In that case, one solution is to pass a ref callback as a normal prop, by giving it a different name:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">Field</span><span class="p">({</span> <span class="nx">inputRef</span><span class="p">,</span> <span class="p">...</span><span class="nx">rest</span> <span class="p">})</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">&lt;</span><span class="nx">input</span> <span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">inputRef</span><span class="p">}</span> <span class="p">{...</span><span class="nx">rest</span><span class="p">}</span> <span class="o">/&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Wrap Field in a higher-order component</span>
<span class="kr">const</span> <span class="nx">EnhancedField</span> <span class="o">=</span> <span class="nx">enhance</span><span class="p">(</span><span class="nx">Field</span><span class="p">);</span>
<span class="c1">// Inside a class component&#39;s render method...</span>
<span class="o">&lt;</span><span class="nx">EnhancedField</span>
<span class="nx">inputRef</span><span class="o">=</span><span class="p">{(</span><span class="nx">inputEl</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="c1">// This callback gets passed through as a regular prop</span>
<span class="k">this</span><span class="p">.</span><span class="nx">inputEl</span> <span class="o">=</span> <span class="nx">inputEl</span>
<span class="p">}}</span>
<span class="o">/&gt;</span>
<span class="c1">// Now you can call imperative methods</span>
<span class="k">this</span><span class="p">.</span><span class="nx">inputEl</span><span class="p">.</span><span class="nx">focus</span><span class="p">();</span>
</code></pre></div>
<p>This is not a perfect solution by any means. We prefer that refs remain a library concern, rather than require you to manually handle them. We are exploring ways to solve this problem so that using an HOC is unobservable.</p>
<div class="docs-prevnext">
</div>
</div>
<div class="nav-docs">
<!-- Docs Nav -->
<div class="nav-docs-section">
<h3>Quick Start</h3>
<ul>
<li>
<a href="/react/docs/installation.html">Installation</a>
</li>
<li>
<a href="/react/docs/hello-world.html">Hello World</a>
</li>
<li>
<a href="/react/docs/introducing-jsx.html">Introducing JSX</a>
</li>
<li>
<a href="/react/docs/rendering-elements.html">Rendering Elements</a>
</li>
<li>
<a href="/react/docs/components-and-props.html">Components and Props</a>
</li>
<li>
<a href="/react/docs/state-and-lifecycle.html">State and Lifecycle</a>
</li>
<li>
<a href="/react/docs/handling-events.html">Handling Events</a>
</li>
<li>
<a href="/react/docs/conditional-rendering.html">Conditional Rendering</a>
</li>
<li>
<a href="/react/docs/lists-and-keys.html">Lists and Keys</a>
</li>
<li>
<a href="/react/docs/forms.html">Forms</a>
</li>
<li>
<a href="/react/docs/lifting-state-up.html">Lifting State Up</a>
</li>
<li>
<a href="/react/docs/composition-vs-inheritance.html">Composition vs Inheritance</a>
</li>
<li>
<a href="/react/docs/thinking-in-react.html">Thinking In React</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Advanced Guides</h3>
<ul>
<li>
<a href="/react/docs/jsx-in-depth.html">JSX In Depth</a>
</li>
<li>
<a href="/react/docs/typechecking-with-proptypes.html">Typechecking With PropTypes</a>
</li>
<li>
<a href="/react/docs/refs-and-the-dom.html">Refs and the DOM</a>
</li>
<li>
<a href="/react/docs/uncontrolled-components.html">Uncontrolled Components</a>
</li>
<li>
<a href="/react/docs/optimizing-performance.html">Optimizing Performance</a>
</li>
<li>
<a href="/react/docs/react-without-es6.html">React Without ES6</a>
</li>
<li>
<a href="/react/docs/react-without-jsx.html">React Without JSX</a>
</li>
<li>
<a href="/react/docs/reconciliation.html">Reconciliation</a>
</li>
<li>
<a href="/react/docs/context.html">Context</a>
</li>
<li>
<a href="/react/docs/web-components.html">Web Components</a>
</li>
<li>
<a href="/react/docs/higher-order-components.html" class="active">Higher-Order Components</a>
</li>
<li>
<a href="/react/docs/integrating-with-other-libraries.html">Integrating with Other Libraries</a>
</li>
<li>
<a href="/react/docs/accessibility.html">Accessibility</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Reference</h3>
<ul>
<li>
<a href="/react/docs/react-api.html">React</a>
<ul>
<li>
<a href="/react/docs/react-component.html">React.Component</a>
</li>
</ul>
</li>
<li>
<a href="/react/docs/react-dom.html">ReactDOM</a>
</li>
<li>
<a href="/react/docs/react-dom-server.html">ReactDOMServer</a>
</li>
<li>
<a href="/react/docs/dom-elements.html">DOM Elements</a>
</li>
<li>
<a href="/react/docs/events.html">SyntheticEvent</a>
</li>
<li>
<a href="/react/docs/test-utils.html">Test Utilities</a>
</li>
<li>
<a href="/react/docs/shallow-renderer.html">Shallow Renderer</a>
</li>
</ul>
</div>
<!-- Contributing Nav -->
<div class="nav-docs-section">
<h3>Contributing</h3>
<ul>
<li>
<a href="/react/contributing/how-to-contribute.html">How to Contribute</a>
</li>
<li>
<a href="/react/contributing/codebase-overview.html">Codebase Overview</a>
</li>
<li>
<a href="/react/contributing/implementation-notes.html">Implementation Notes</a>
</li>
<li>
<a href="/react/contributing/design-principles.html">Design Principles</a>
</li>
</ul>
</div>
</div>
</section>
<footer class="nav-footer">
<section class="sitemap">
<a href="/react/" class="nav-home">
</a>
<div>
<h5><a href="/react/docs/">Docs</a></h5>
<a href="/react/docs/hello-world.html">Quick Start</a>
<a href="/react/docs/thinking-in-react.html">Thinking in React</a>
<a href="/react/tutorial/tutorial.html">Tutorial</a>
<a href="/react/docs/jsx-in-depth.html">Advanced Guides</a>
</div>
<div>
<h5><a href="/react/community/support.html">Community</a></h5>
<a href="http://stackoverflow.com/questions/tagged/reactjs" target="_blank">Stack Overflow</a>
<a href="https://discuss.reactjs.org/" target="_blank">Discussion Forum</a>
<a href="https://discord.gg/0ZcbPKXt5bZjGY5n" target="_blank">Reactiflux Chat</a>
<a href="https://www.facebook.com/react" target="_blank">Facebook</a>
<a href="https://twitter.com/reactjs" target="_blank">Twitter</a>
</div>
<div>
<h5><a href="/react/community/support.html">Resources</a></h5>
<a href="/react/community/conferences.html">Conferences</a>
<a href="/react/community/videos.html">Videos</a>
<a href="https://github.com/facebook/react/wiki/Examples" target="_blank">Examples</a>
<a href="https://github.com/facebook/react/wiki/Complementary-Tools" target="_blank">Complementary Tools</a>
</div>
<div>
<h5>More</h5>
<a href="/react/blog/">Blog</a>
<a href="https://github.com/facebook/react" target="_blank">GitHub</a>
<a href="http://facebook.github.io/react-native/" target="_blank">React Native</a>
<a href="/react/acknowledgements.html">Acknowledgements</a>
</div>
</section>
<a href="https://code.facebook.com/projects/" target="_blank" class="fbOpenSource">
<img src="/react/img/oss_logo.png" alt="Facebook Open Source" width="170" height="45"/>
</a>
<section class="copyright">
Copyright © 2017 Facebook Inc.
</section>
</footer>
</div>
<div id="fb-root"></div>
<script src="/react/js/anchor-links.js"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-41298772-1', 'facebook.github.io');
ga('send', 'pageview');
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.6&appId=623268441017527";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
docsearch({
apiKey: '36221914cce388c46d0420343e0bb32e',
indexName: 'react',
inputSelector: '#algolia-doc-search'
});
</script>
</body>
</html>