The way of a Higher-Order Component

July 20, 2017 0 Comments

The way of a Higher-Order Component

 

 

A higher-order component (HoC) is an advanced technique in React for reusing component logic. It is absolutely not from a React World, but by some reason realisation is entangled.

HOC is just a function, which gonna get one function as input and return other as output. And add some magic between them, of course.
A Component wrapped by dozen side effects.

Higher-Order Components can be used for:

  1. Extending functionality.
  2. Providing functionality.
  3. Adding side effects.
  4. Adding visual effects.

Most of this cases can be found on Facebook page:

So, you can had componentDidMount/componentDidUnmount in a smart HOC, and use dumb payload component. Keep you code simple!

You can provide props from HOC to payload, as redux connect does. Make things more handy!

You can send analytics events from HoC. Who else will apply a side effect?

Or you can wrap your dump component with smart scrollable one, using HoC. Why not?

HOC is just a tool, just an approach. It can do anything you want. And also can do anything you need.

The first way of HOC is a way of a Wrapper.

And ideal (from Facebooks point of view) HoC is a wrapper.</p><pre id="6847" class="graf graf--pre graf-after--p">const logProps =&gt; (WrappedComponent) =&gt;<br> class extends React.Component {<br> componentWillReceiveProps(nextProps) {<br> console.log(&apos;Current props: &apos;, this.props);<br> console.log(&apos;Next props: &apos;, nextProps);<br> }<br> render() {<br> // Wraps the input component in a container, without mutating it. Good!<br> return &lt;WrappedComponent {...this.props} /&gt;;<br> }<br> }<br>}</pre><p id="09da" class="graf graf--p graf-after--pre">This is a <code class="markup--code markup--p-code">real</code> higher order function, as long it getting a function as argument, and returns another function. And this is most popular case not only in react world, but in any other language.</p><p id="8ca7" class="graf graf--p graf-after--p">You do not modify source component, you just wrap it with yet another component.</p><p id="f6b4" class="graf graf--p graf-after--p">In some cases you will return a <code class="markup--code markup--p-code">new</code> component with a <code class="markup--code markup--p-code">new</code> behavior.</p><p id="4b3a" class="graf graf--p graf-after--p">In some cases you will return an <code class="markup--code markup--p-code">old</code> component, with <code class="markup--code markup--p-code">old</code> behavior. So HoC will be <code class="markup--code markup--p-code">transparent</code>.</p><p id="1b8a" class="graf graf--p graf-after--h3">The second way of HOC is a way of a <strong class="markup--strong markup--p-strong">Markup</strong>.</p><pre id="ab67" class="graf graf--pre graf-after--p">const scrollable = (Component) =&gt; (props) =&gt; (<br> &lt;div className=&apos;superScrollable&apos;&gt;<br> &lt;div className=&apos;scrollableContent&apos;&gt;<br> &lt;Component {...props} /&gt;<br> &lt;/div&gt;<br> &lt;/div&gt;<br>)</pre><p id="9ea5" class="graf graf--p graf-after--pre">This is also HoC&#x200A;&#x2014;&#x200A;it is getting a component as input, and returning a component as output. The difference from first way is in <strong class="markup--strong markup--p-strong">internal realization</strong>. There is no difference for a API consumer.</p><p id="11cb" class="graf graf--p graf-after--p">Why you should write a code like this?</p><blockquote id="9e56" class="graf graf--pullquote graf-after--p">Why not use a first&#xA0;way?</blockquote><p id="d22e" class="graf graf--p graf-after--pullquote">Because YOU CAN! And this is good example of a <code class="markup--code markup--p-code">component</code> approach. If you gonna wrap a <code class="markup--code markup--p-code">Target</code> component with some markup&#x200A;&#x2014;&#x200A;probably you should do it via <code class="markup--code markup--p-code">HoC</code>. Treat this a <strong class="markup--strong markup--p-strong">programming pattern</strong>.</p><blockquote id="52d8" class="graf graf--blockquote graf-after--p">Do something with a given Component, and return a new Component.</blockquote><p id="72c3" class="graf graf--p graf-after--blockquote">Just keep in mind&#x200A;&#x2014;&#x200A;HoC is a <strong class="markup--strong markup--p-strong">extenal behavior</strong> for a function. It <strong class="markup--strong markup--p-strong">does not</strong> <strong class="markup--strong markup--p-strong">limit</strong> you with <strong class="markup--strong markup--p-strong">actual realization</strong>. It can provide just a markup. It can do anything they designed to do. Just try to find a good reason.</p><p id="5e13" class="graf graf--p graf-after--h3">Keep in mind&#x200A;&#x2014;&#x200A;in most of cases HOC will return something look like original component. <strong class="markup--strong markup--p-strong">Behave silently</strong>. Just doing its duty in the shadows…

To achieve this you often have to pass thought HOC propTypes and rest of static methods, which may exists in original Component.

You can found more information about this at the end of Facebook`s article.

The third way of HOC is a NOT HOC way. It is a Component Way.

Just recall the first way — we have created a brand new class. And what if..

const logProps => (WrappedComponent) => (props)
<LogProps WrappedComponent={WrappedComponent} props={props} />
} // PS this is just a markup
class LogProps extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
}
render() {
const {WrappedComponent, props} = this.props;
// Wraps the input component in a container
return <WrappedComponent {...this.props} />;
}
}

It is same.. but different! But let me show you a little trick

<LogProps props={props}><WrappedComponent/><LogProps>

LogProps will take Component as prop, and return Component as result. Just remove JSX magic and this code will become a code, not a JSX markup.

But this a HoC — higher order Component, not as function as it did before.
It still consumes Component as input(as children), and renders Component as output.

The goal is simple — now we can use HOC as a normal component. And, sometimes, we can create much more complex composition, as long we can control all settings, props, and order from a one place and at any depth.

we can control all settings, props, and order from a one place and at any depth.

And this type of HoC will be a very react-hot-loader -- compatible, as long only Components friendly to react-hot-loader, and function-based HoC — not always 😭.

I had an article about it:

Just dont forget about this feature. It can provide a faster and more semantic code.

Code will be different, but result — result will be same.

Also, I have to point one moment.

Do not trust Facebook. Just a year ago they said to use mixings. Now they said that it was wrong. That they will say tomorrow. (only sith think in absolutes)

As they said — HoC is a way to extend behavior of a Component without using OOP, so inheritance.

And they have a good example for it:

function logProps(InputComponent) {
InputComponent.prototype.componentWillReceiveProps = function(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
};
// The fact that we're returning the original input is a hint that it has
// been mutated.
return InputComponent;
}
// EnhancedComponent will log whenever props are received
const EnhancedComponent = logProps(InputComponent);

So, one (a very stupid one) is going to override prototype of original class.

What the fuck is going on?

Just remember — as long you can inherit your Components from React.Component — you can inherit something else from your Component. And this is a right example for inherence in HoC.

function logProps(InputComponent) {
return class LogProps extends InputComponent { //<<--
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
super.componentWillReceiveProps(nextProps);
}
}
}

There is a lot of talks about “which flower has a better color" — composition or OOP, but both sides are just tools for you.

You can choose.

In some cases inheritance is better:

  1. You have much more control. (too much, OOP haters said)
  2. Only one component as result. (reduce DOM-tree size by factor of 2)
  3. React-hot-loader compatible.

But a lot of brogrammers will not respect this approach, you have been warned :)

const Conclusion = 
publishOnMedium(checkSyntax(makeAGoodStory(text)))

HOC is awesome, HOC is usable, debagable and just beautiful.

They enables single responsibility principle by keeping each part in a separate HoC and having them all, in the right order, as result.


Tag cloud