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 |
This comment has been minimized.
This comment has been minimized.
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. :) |
This comment has been minimized.
This comment has been minimized.
s/date/data |
This comment has been minimized.
This comment has been minimized.
I think you're missing 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 |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
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!! |
This comment has been minimized.
This comment has been minimized.
@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:
|
This comment has been minimized.
This comment has been minimized.
I think most cases for mixins can be covered with this approach. when does 0.13 get stable? |
This comment has been minimized.
This comment has been minimized.
@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 |
This comment has been minimized.
This comment has been minimized.
Side question: I'd love to be able to do this: is the 6to5 experimental flag safe to use for just this?? |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
A few observations for readers freaked out by this:
Hope this helps! |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
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) |
This comment has been minimized.
This comment has been minimized.
https://github.com/kriasoft/react-decorators - a collection of higher-order React components |
This comment has been minimized.
This comment has been minimized.
How do you specify MyComponent.defaultProps in this setup? |
This comment has been minimized.
This comment has been minimized.
@Frikki Something like this (untested):
Ideally also with a displayName so it appears nicely in the React devtools |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
@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. |
This comment has been minimized.
This comment has been minimized.
@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 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 — |
This comment has been minimized.
This comment has been minimized.
@sebmarkbage, @ALL // Initialize the public class
var inst = new Component(publicProps, publicContext); ..hile all the samples' costructors really ignore these I'd rather them: constructor(publicProps, publicContext) {
super(publicProps, publicContext);
this.state = { data: null };
} Like, always. |
This comment has been minimized.
This comment has been minimized.
@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); |
This comment has been minimized.
This comment has been minimized.
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);
} |
This comment has been minimized.
This comment has been minimized.
A lot of implementations, not enough discussion. |
This comment has been minimized.
This comment has been minimized.
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 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 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~ |
This comment has been minimized.
This comment has been minimized.
@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:
Here, the 'Cat' class is actually the EnhancedComponent class and the render method in Cat class would not work! |
This comment has been minimized.
This comment has been minimized.
@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 |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
This Gist looks like reinvention of the classic Dependency Injection design pattern.
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. |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
Yep. I assume people will screw up using the approach.
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. |
This comment has been minimized.
This comment has been minimized.
What's the difference between HOC and |
This comment has been minimized.
This comment has been minimized.
I have no idea what you all above posted because none of them worked. Did you test them? This works .......
|
This comment has been minimized.
This comment has been minimized.
@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. |
This comment has been minimized.
This comment has been minimized.
Thoughts on using something like |
This comment has been minimized.
This comment has been minimized.
domcom(https://github.com/taijiweb/domcom) can have true inheritance. |
This comment has been minimized.
This comment has been minimized.
This are If your higher order component need
For debugging purpose with React Tools I match function name with class name, Ex: If you don´t need
Same like first snip, I use a named function |
This comment has been minimized.
This comment has been minimized.
I've written about some real world examples of higher order components we use in our application here: Comments are more than welcome |
This comment has been minimized.
This comment has been minimized.
I use this way 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? |
This comment has been minimized.
This comment has been minimized.
I created a forked showing how I used this with ES7 decorators: (Also fixes a few issue with this gist) |
This comment has been minimized.
This comment has been minimized.
I created two packages which you can use for creating of high-order components. Both libraries will create function compatible with ES7 decorators as well. |
This comment has been minimized.
This comment has been minimized.
Hey guys! you can checkout my example. import React, { Component } from 'react'; let HOC = WrappedComponent => { let App = HOC((props) => { ReactDOM.render( |
This comment has been minimized.
This comment has been minimized.
@seeden |
This comment has been minimized.
This comment has been minimized.
Interesting examples, is there any particular advantage of attempting to enforce OO over HOC? Anyhow, fun to follow the thread. Pedantic point of note, why in all the ES6 / ES7 examples are there still references to "var"? Also arrow functions implicitly return whatever is after them, so if no multi-expressions are needed, then no need for the curlies! Anyhow.. I digress.... |
This comment has been minimized.
This comment has been minimized.
I love this gist, because it shows a simple example for a higher order component. I am not sure if this is the perfect place, but maybe people are interested to read a more elaborated gentle introduction to higher order components when they come across this gist looking for HOC examples. |
This comment has been minimized.
This comment has been minimized.
Hello everyone, I came across a good blog which is about React Higher Order components. It was completely awesome and explained in well manner. Have a look into it: Link |
This comment has been minimized.
This comment has been minimized.
Thanks for the links |
This comment has been minimized.
This comment has been minimized.
is this the first ever written HOC ? |
This comment has been minimized.
This comment has been minimized.
Wow, the React community really flubbed on terminology here. In this gist, the component utilizing what we call the HoC is called the HoC, whereas the thing we call the HoC is called an "Enhancer." This makes way more sense, as a "HoC," as it is known today, is not itself a component at all. Sad!! |
This comment has been minimized.
This comment has been minimized.
Not quite see the difference between HoC and old decorator pattern. I would say HoC is a nice react implementation of decorator pattern. |
This comment has been minimized.
This comment has been minimized.
Your constructor needs |
This comment has been minimized.
lovely :)
curious as to why not export default:
and then import it directly:
unless you want to export more from the
Enhance
module?