Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Higher-order Components
import { Component } from "React";
export var Enhance = ComposedComponent => class extends Component {
constructor() {
this.state = { data: null };
}
componentDidMount() {
this.setState({ data: 'Hello' });
}
render() {
return <ComposedComponent {...this.props} data={this.state.data} />;
}
};
import { Enhance } from "./Enhance";
class MyComponent {
render() {
if (!this.data) return <div>Waiting...</div>;
return <div>{this.data}</div>;
}
}
export default Enhance(MyComponent); // Enhanced component
stryju commented Feb 11, 2015

lovely :)


curious as to why not export default:

export default ComposedComponent => class extends Component {

and then import it directly:

import Enhance from './Enhance'

unless you want to export more from the Enhance module?

Owner

Just to be able to name it for debugging and stuff.

export default function Enhance(ComposedComponent) {
  return class extends Component {
    ...

But that's not a one-liner. Anyway... That's besides the point. :)

s/date/data

I think you're missing extends Component for MyComponent.

Also, you could avoid the intermediate step:

import { Enhance } from "./Enhance";

var MyComponent = Enhance(class extends Component {
  render() {
    if (!this.data) return <div>Waiting...</div>;
    return <div>{this.data}</div>;
  }
});

export default MyComponent; // Enhanced component

ES7 decorators (assuming they are still discussed and if they will work like Python decorators) can help here, I think:

import { Component } from "react";
import { Enhance } from "./Enhance";

@Enhance
class MyComponent extends Component {
  render() {
    if (!this.data) return <div>Waiting...</div>;
    return <div>{this.data}</div>;
  }
}

export default MyComponent; // Enhanced component
arqex commented Feb 12, 2015

That' classical OO inheritance. It is really cool to have it to create high-order components, but please don't remove the mixins from react. Mixins offer a lot of flexibility when developing.

Imagine I have 3 features in 3 mixins that pass to my components indepenently. Some component use 2 mixins, others one.... I would need to create 7 high-order components to get all the variations I get with no effort using mixins.

Save the mixin!

Thanks for react, it is really great!!

@arqex you don't need to create 7 higher order components because you can apply one higher order component to the result of the application of another:

var ResultedComponent = EnhanceWithRainbow(EnhanceWithFlowers(OriginalComponent))
nmn commented Feb 13, 2015

I think most cases for mixins can be covered with this approach.
cool!

when does 0.13 get stable?

@arquex; his example wasn't to highlight the inheritance angle (that's just a new way to declare react components with es6 class syntax); he was showing how you can use higher order functions in lieu of mixins

nmn commented Feb 13, 2015

Side question:

I'd love to be able to do this:
<ComposedComponent {...this.props} />

is the 6to5 experimental flag safe to use for just this??

fubhy commented Feb 13, 2015

There's valid uses cases for both Traits as well as extending. Both serve a very different purpose and can (and should) co-exist. Things like the various different flavors of Flux utility Mixins could very well be covered with the approach shown here. I like it.

sporto commented Mar 12, 2015

This is nice, a safer approach than mix-ins, mixins have the nasty problem of clashes between them, this approach seems to totally make this an non-issue

gaearon commented Mar 12, 2015

A few observations for readers freaked out by this:

  • this.data in wrapper component is probably supposed to be this.props.data (unless React plans to put props on instance fields in the future). It's not being “written” from wrapping component, there's no inheritance there.
  • extends Component is needed in the wrapper just to get setState. It's not extending the wrapped component, it's extending base React.Component to get setState.

Hope this helps!

rektide commented Mar 21, 2015

Mixins are respectful of Last Responsible Moment principles, and give a polymorphism ala carte. Composition creates deeply nested, hard to restructure, complect systems. There are plenty of problems that using mixins could create, and we can hype ourselves up for how bad loose/informal can be, but the nice thing is that structurally our system is engineered flat, and we can pick and choose how to address who makes what available where and how intelligently, whereas with composition each layer has tightly fit constraints & responsibilities that are much harder to shift around and adapt.

I look forward to seeing more mixin attempts grow. We're pretty short on well reasoned ones now. StampIt is one example of a relatively well thought out one.

data={this.state.data} should come before {...this.props} or else it gets overridden, right?

brigand commented Apr 3, 2015

This is so last I-thought-es6-was-done-with-breaking-changes. Up to date: https://gist.github.com/brigand/1c476f365a503d5a1e2d

(also changed import "React" which babel doesn't like)

koistya commented May 5, 2015

https://github.com/kriasoft/react-decorators - a collection of higher-order React components

Frikki commented May 7, 2015

How do you specify MyComponent.defaultProps in this setup?

Daniel15 commented May 7, 2015

@Frikki Something like this (untested):

import { Component } from 'React';

export var Enhance = ComposedComponent => {
  class NewComponent extends Component {
    constructor() {
      this.state = { data: null };
    }
    componentDidMount() {
      this.setState({ data: 'Hello' });
    }
    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  }
  NewComponent.propTypes = {
    // whatever
  };
  return NewComponent;
};

Ideally also with a displayName so it appears nicely in the React devtools

koistya commented May 12, 2015
withData.js (higher-order React component)
import React, { Component } from 'react';

function withData(ComposedComponent, name) {
  return class Data extends Component {
    state = { data: null };
    componentDidMount() {
      this.setState({ data: `Hello, ${name}!` });
    }
    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };
}

export default withData;
MyComponent.js (stateless functional React component)
import React from 'react';
import withData from './withData';

function MyComponent({ data }) {
  if (!this.data) {
    return <div>Waiting...</div>;
  }
  return <div>{this.data}</div>;
}

export default withData(MyComponent, 'Test');
Source: React Starter Kit

@sebmarkbage - Thanks for sharing this!

Here's an alternative approach that I think has some advantages over the higher-order component solution you've described here. I'd greatly appreciate your input.

jmurzy commented May 16, 2015

@aldendaniels Although I'd say composition is a much safer alternative in most situations, I've been using a similar approach to what you're proposing in production for months, and it's been working great.

let as = T => (...traits) => traits.reverse().reduce((T, M) => M(T), T);
class Mojito extends as(Component)(Sugar, Ice, Rum("Facundo Paraiso"), Mint, Soda) { }

Note that you can also pass in arguments to a trait as in Rum("Facundo Paraiso").

Unfortunately, this results in "diamond inheritance" when both Soda and Rum also extend from, for example, Liquid. This could possibly be mitigated by using something similar to Scala's trait linearization or C3 linearization.

On a side note, I was also able to use vanilla classes as traits in a somewhat more verbose version of as with a wrapper — reduce((T, M) => WRAP(M)(T), T).

@sebmarkbage, @all
Can anyone please tell me, why is it, component instantiation code being..

    // Initialize the public class
    var inst = new Component(publicProps, publicContext);

..hile all the samples' costructors really ignore these publicProps and publicContext?

I'd rather them:

  constructor(publicProps, publicContext) {
    super(publicProps, publicContext);
    this.state = { data: null };
  }

Like, always.

gyzerok commented Jun 16, 2015

@sebmarkbage, @all What do you think about following implementation?

export function Enhance(ComposedComponent: Component, Mixin: Component): Component {
  return class extends Mixin {
    render() {
      return <ComposedComponent {...this.props} {...this.state} />;
    }
  }
}
import React, { Component } from 'react';
import { Enhance } from './Enhance';

class SomeMixin extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = { data: null };
  }
  componentDidMount() {
    this.setState({ data: 'Hello' });
  }
}

class SomeView extends Component {
  render() {
     return <div>{this.props.data + ', world!'}</div>;
  }
}

export default Enhance(SomeView, SomeMixin);
gyzerok commented Jun 22, 2015

If somebody is intrested, I've end up with the following implementation of Enhance:

export function Enhance(ComposedComponent: Component, mixins: Array<Component>): Component {
  return mixins.reduce((ComposedComponent, Mixin) => {
    return class extends Mixin {
      render() {
        return <ComposedComponent {...this.props} {...this.state} />;
      }
    }
  }, ComposedComponent);
}
xtrasmal commented Sep 5, 2015

A lot of implementations, not enough discussion.

namuol commented Sep 16, 2015

HOCs are great and I use them all the time, but let's be careful not to conflate them with decorators.

That is, HOCs are not decorators, especially in the canonical sense of the term.

A HOC using the above Enhance method implicitly "masks" any properties defined on the original class (static, prototype, or instance methods/properties are not part of the wrapped API).

Canonical decorators leave the original API surface of its input intact and only adds behavior:

export default function hasBar(Base) {
  return class extends Base {
    bar() { return 'baz'; }
  }
}
export default function hasQuux(Base) {
  return class extends Base {
    quux() { return 'biz'; }
  }
}
import hasBar from './hasBar';
import hasBar from './hasQuux';

@hasBar
@hasQuux
export default class Foo {
  answer() { return 42; }
}
import Foo from './Foo';

let f = new Foo();

f.quux(); // "biz"
f.bar(); // "baz"
f.answer(); // 42

If you're careful, this approach can work nicely with components and maintain the original component's API-surface:

export default function doesSomethingOnMount(Component) {
  return class extends Component {
    static displayName = Component.displayName || Component.name;

    componentDidMount() {
      super.componentDidMount && super.componentDidMount();
      // do your stuff here
    }
  };
}
import doesSomethingOnMount from './doesSomethingOnMount';

@doesSomethingOnMount
class Foo extends Component {
  bar() { return 'baz'; }
  // ...
}
<Foo ref="foo" onClick={() => { alert(this.refs.foo.bar()); }} />

Of course, in many (probably most) cases it might make more sense to use HOCs, but this is a useful pattern if your component needs to maintain its original API surface when enhanced.

If your extension adds a new propType to your component, for instance, consider using something like Object.assign to extend the original version of propTypes, and politely warn the user if you override any of their existing propTypes:

export default function hasFooProp(Component) {
  const displayName = Component.displayName || Component.name;

  if (Component.propTypes && Component.propTypes.foo) {
    console.warn(`Warning: `hasFooProp` is overriding the original \`displayName.propTypes.foo\`.`);
  }

  return class extends Component {
    static displayName = displayName;

    static propTypes = Object.assign({}, Component.propTypes, {
      foo: React.PropTypes.string,
    });
  };
}

However, if you find yourself carefully overriding/merging many parts of the original component's API surface like with the above example, it's a good sign that a HOC is the more appropriate solution.

Cheers~

@namuol Thanks for your detailed explaination! I just ran into this issue and I didn't even realize it before. This gets especially tricky when you have something like:

@myEnhance
class Cat {
    static meow = event => {
        return 'meow!';
    }
    render (){
        return (<div>{{Cat.moew()}}</div>);
    }
}
export default Cat;

function myEnhance {
    return (SomeComponent) => class EnhancedComponent extends React.Component {
        render() { 
            return <Cat />;
        }
    }
}

Here, the 'Cat' class is actually the EnhancedComponent class and the render method in Cat class would not work!

arush commented Sep 19, 2015

@namuol thanks for pointing out those gotchas, great advice.

An alternative to HOCs and decorators, although not idiomatic React, is to use the excellent react-stampit which works extremely well for solving the mixin issue, as it favors composition over inheritance

acdlite commented Oct 8, 2015

Yesterday I released Recompose, which takes the higher-order components concept and runs with it. I've been writing React components in this style for a while now, and I think it covers a vast majority of use cases. Certainly there are other things React can do to improve its API — I'm especially interested in efforts to externalize the state tree — but it's impressive how much can be accomplished using the basic component architecture. Really speaks to how much React has gotten right, even from the start.

https://github.com/acdlite/recompose

koresar commented Oct 11, 2015

This Gist looks like reinvention of the classic Dependency Injection design pattern.

class Car
{
    private Engine;
    private SteeringWheel;
    private Tires tires;

    public Car(Engine engine, SteeringWheel wheel, Tires tires)
    {
        this.Engine = engine;
        this.SteeringWheel = wheel;
        this.Tires = tires;
    }
}

It's getting messy in complex (read most) apps where an object (read component) dependency tree is hard to reason about.

Just imagine your HOCs depend on other HOCs which depend on another HOC which depend on few other HOCs ... and so on. That is tight coupling - the forever problem the classical inheritance suffer from.

Owner

It is not quite the same because you can chain HoC where each one is independent. It is easy to screw this up though.

This pattern was designed specifically for things like Relay which just pass props straight through. It has since been picked up and abused for other patterns.

koresar commented Oct 11, 2015

It is easy to screw this up though.

Yep. I assume people will screw up using the approach.

This pattern was designed specifically for things like Relay ...
It has since been picked up and abused for other patterns.

This reminds me the MVC pattern. It was designed for a single small purpose, but blown out to be an architecutre because people abused it.

What's the difference between HOC and this.props.children aside of the syntax? A parent component can modify the children's props, abstract state, and control the way the child instance renders. It'll be interesting to know what are the things both patterns have in common and where do they differ

I have no idea what you all above posted because none of them worked. Did you test them? This works .......

 import React from 'react';
 import ReactDOM from 'react-dom';
 import { MyEnhance } from "./enhance";

 @MyEnhance
 class MyComponent extends React.Component {
   render() {
     if (!this.props.data) return <div>Waiting...</div>;
     return <div>{this.props.data}</div>;
   }
 };

 ReactDOM.render(<MyComponent />, document.getElementById('root'));

 import React, { Component } from "React";

 export var MyEnhance = ComposedComponent => class extends Component {
   constructor() {
     super();
     this.state = { data: null };
   }

 componentDidMount() {
     this.setState({ data: 'Hello You' });
   }

 render() {
     const temp = this.state.data;
     return <ComposedComponent {...this.props} data={ temp } />;
   }
 };
passcod commented Jan 23, 2016

@JoeGrasso ES6 changed in between the original post and your reading, thus it all did work just fine at the time. See about halfway through the thread for someone commenting about pretty much the same thing you did.

Thoughts on using something like static displayName = "HigherOrderComponentName["+(Component.displayName || Component.name)+"]"; so it shows up like <HigherOrderComponentName[ComposedComponentName] ...> in the dev tools?

domcom(https://github.com/taijiweb/domcom) can have true inheritance.

bySabi commented May 6, 2016

This are minimal skeleton for higher order components

If your higher order component need state or lifecycle methods use this:

const hoc = C => class _hoc extends React.Component {
    render() {
      return <C { ...this.props }/>;
    }
  }

For debugging purpose with React Tools I match function name with class name, Ex: hoc match _hoc. This way I know what is a "real" component and what a HOC, it looks like a function.

If you don´t need state or lifecycle methods on your HOC this snip is more functional:

const hoc = C => function _hoc(props) {
    return <C { ...props }/>;
  }

Same like first snip, I use a named function _hoc maching hoc for debugging purpose.

I've written about some real world examples of higher order components we use in our application here:
http://techblog.realestate.com.au/reactjs-real-world-examples-of-higher-order-components/

Comments are more than welcome

MerlinYu commented Jun 3, 2016

I use this way
var MyComponent = Enhance(class extends Component {
render() {
if (!this.data) return

Waiting...
;
return
{this.data}
;
}
});
export default MyComponent; // Enhanced component

But it meets a problem "Minified exception occurred;use the non-minfied dev environment for the full error message and additional helpful warnings." How to deal with this probleam?

I created a forked showing how I used this with ES7 decorators: (Also fixes a few issue with this gist)
https://gist.github.com/jeromepl/2f7df563f273563261690221c22aa0af

seeden commented Jun 24, 2016

I created two packages which you can use for creating of high-order components.
With react-provide-props you can easily create high-order component which will add props to other components. It will create state-less high-order compoment.
Second package react-high-order-provider you can use for state-full high-order components where you can define your own logic.

Both libraries will create function compatible with ES7 decorators as well.

Musbell commented Jul 9, 2016 edited

Hey guys! you can checkout my example.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

let HOC = WrappedComponent => {
return class extends Component{
constructor(props){
super(props);
this.state = {
name : ' '
};
this.onNameChange = this.onNameChange.bind(this)
}
onNameChange(e){
this.setState({
name : e.target.value
})
}
render(){
const newProps = {
value : this.state.name,
onChange : this.onNameChange
};
return <WrappedComponent {...this.props} {...newProps} {...this.state}/>
}
}
};

let App = HOC((props) => {
return(
<div> <input type="text" {...props}/> <p>{props.name}</p> </div>
)
});

ReactDOM.render(
<App />,
document.getElementById('root')
);

Musbell commented Jul 9, 2016

@seeden
I love the libraries, they are both great 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment