-
Notifications
You must be signed in to change notification settings - Fork 46.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for porting React's Mixin APIs to a generic primitive #1380
Comments
Just pointing out that class C extends mixin(A, B) { // TS: Error This will be useful for ES6 classes. But will not make it easy for people using TypeScript. |
That's a good point. I don't think there is a way to create composable interfaces in this way in TypeScript. Do you see any alternatives? |
In typescript the only way I see to implements something similar to what you propose would be something like that : declare function applyMixins(target: any, mixins: any []) : void;
declare function superMixin<T>(t:T): T;
class A {
componentDidMount() { }
myMethodA() { return 'A' }
}
class B {
componentDidMount() { }
myMethodB() { return 'B' }
}
class C implements A,B {
componentDidMount(): void {
superMixin(this).componentDidMount()
console.log('C');
}
myMethodA: () => string;
myMethodB: () => string;
}
applyMixins(C, [A, B]); I already tried to wrap react logic with es6 class for typescript usage and obtained the following result : https://github.com/fdecampredon/react-typescript with similar way of doing that. By the way mixin are in the roadmap of typescript for 1.x, but I have no clue when they plan to implement that. (I guess not soon) |
I don't see a better way than what @fdecampredon is recommending. Notice his |
Luckily mixins are completely optional and you can use whatever system you want. Presumably TypeScript would have to build in similar features and you can just use that. They're even frowned upon when they're overused so they're completely optional. handwave, punt |
So is this on the road map for React soon then? |
@pspeter3, my understanding is that it is targeted for version 0.12 as part of Components as ES6 Classes. To guestimate the timeframe, take a look at the tags. Really looking forward to this! We want to create React components in code using TypeScript. |
@ctaggart I'm in the same place. Is there anyway to help get this out faster? (Eg, which issues would need to be resolved so I can create Pull Requests for them) |
Is there a possibility for something less inheritence based in this? Seems like doing something a la Traits would be a more composition-ally focused approach. My worry is that this makes for a Ruby-esque problem of clever module ordering in order to get the right combination of super calls. It also means that you need to know that you are overriding a method in your mixin which makes mixins less reusable. An alternative would be to just require the class definer to resolve conflicts for non lifestyle methods/props and provide some utility functions for making that really easy? Admittedly I don't know the limitations of the es6 class syntax but in the current one
|
@theporchrat The code in OP is based on ECMAScript proposals (although changing from day to day it seems). |
@syranide the class syntax is from es6 proposals, but a Sure one can mixin however they want, since the component is just a constructor and prototype, which is great, except that it destroys the interoperability of mixins. It is definitely possible to have a mixin system that is not es6, but works with es6 (which is what I took goal here to be). All that is needed for the class syntax is a It just strikes me that the above |
@theporchrat The |
ooo that is interesting thanks for the link |
React Core itself is not opinionated about how you build your classes. Generally we prefer composition. This is already possible using components. This proposal is about providing feature parity with current React classes. It's not an encouraged pattern. Native trait features in the ECMAScript language will likely have similar feature sets.
|
So for third party languages, http://facebook.github.io/react/blog/2014/10/14/introducing-react-elements.html#third-party-languages, will the class in |
@pspeter3 0.12 doesn't support classes in this way yet, but once we support ES6 classes then |
@spicyj I understand that, I meant for 0.13. Will I need to pass the ES6 class into |
@pspeter3, no you will not need to do that. |
@sebmarkbage Oh that's awesome! |
It seems that ES6 classes are being actively worked on and I presume targeted for v0.13 (can anyone confirm that?). If so, what is the current plan for ES6 classes and mixins in that release? I'm assuming React Core will not ship with any magic to somehow handle mixins with |
Yes, they are scheduled for 0.13. In the 0.13 release, we will not ship this mixin helper. It requires |
@sebmarkbage is this still the case? React.createClass is still the preferred way of doing things in 0.13? I don't think http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html really illustrates that opinion, and maybe that is why there is a lot of confusion. |
@rtorr We are moving towards native ES6 classes as the "modern" way of doing things. That said, many people (including a large portion of Facebook) will continue to use React.createClass for some time, so both options will be supported in the near term. |
Was wondering if subclassing can solve it for now. A 'mixin' can extend |
Scala syntax looks good o_O |
@sebmarkbage I think I've come up with a feasible solution: traits.js import traits from 'es6-traits';
import React from 'react/addons';
export const {on, using} = traits();
export const autobind = {
[Symbol.toStringTag]: 'autobind',
constructor() {
Object.getOwnPropertyNames(this.constructor.prototype)
.filter(x => x.startsWith('on'))
.map(x => this[x] = this[x].bind(this));
}
};
export const purerender = Object.assign(React.addons.PureRenderMixin, {
[Symbol.toStringTag]: 'purerender'
}); some-component.js import React from 'react';
import {on, using, autobind, purerender} from './traits';
export default class SomeComponent extends (on (React.Component), using (autobind, purerender)) {
...
} The syntax is a bit longer than the proposed The package is available at es6-traits and is based on @brigand's excellent |
Those syntax hacks are impressive :-) I like it, but looking over the source I think the second example needs to be this because import React from 'react';
import traits from 'es6-traits';
import {autobind, purerender} from './traits';
const {on, using} = traits();
export default class SomeComponent extends (on (React.Component), using (autobind, purerender)) {
...
} I also like the use of constructor over componentWillMount in the traits. It feels much more pure. |
@brigand It can be shared, you can also specify different resolution rulesets for different classes like this: export const {on, using} = traits({
ruleset: {
Cat: {
sleep: 'ONCE',
play: 'MANY'
},
Dog: {
sleep: 'ONCE',
play: 'ONCE'
}
}
}); I need to find some time to come up with a document and add browser support so it can be played with inside JSBin etc. but while we are at it, any suggestion to the API and how the lib should work are welcome. |
I followed up in gsklee/estraits#2. |
It would seem to me that unless React is going to move to an event or hook based system for the lifecycle methods, those methods need to be handled specifically. You can't just mix them into the prototype, which means whatever the solution it's going to be React specific. I like something like the following where it is clear that I am extending a React.Component with PureRenderMixin mixed in. // user's code
class MyComponent extends React.Component(PureRenderMixin) {
//...
}
// ReactComponent.js
function ReactComponent(props, context) {
if(!(this instanceof ReactComponent)) {
if(arguments.length) return createMixedComponent(arguments)
return ReactComponent
}
this.props = props;
this.context = context;
} |
@mlrawlings That looks great! |
Here's an implementation of @mlrawlings's syntax http://jsbin.com/tofidoxucu/2/edit?js,console,output It does look much cleaner. If I switch to es6 classes, I'll probably write it like that, and have a small transform to move it out of there. Early errors aren't worth giving up imo, but luckily we don't need to compromise :-) |
In my opinion higher order components and the new |
@gaearon Looks promising, but a little bit too React-specific. @mlrawlings's example is the most ideal but it'd require manually wrapping all classes with a function; I wonder if there is any possible way to abstract that part out into a generic lib? |
there will be TC39 proposal for annotations microsoft/TypeScript#1557 (comment) can we use something like that to abstract mixins? |
@chicoxyzzy it doesn't really add any value, other than being able to declare the mixins before the class in code. Annotations don't do anything, so you need something else to act on them. With classes, either directly on the class via // used for an instanceof check, because other annotations may be used
class ReactMixin {
constructor(mixin){ this.mixin = mixin }
}
@ReactMixin(PureRenderMixin)
class Foo extends React.Component {
...
}
applyMixins(Foo);
function applyMixins(Class){
if (Class.annotate) {
Class.annotate.forEach(maybeMixin => {
// apply the mixin behavior for each mixin annotation
if (maybeMixin instanceof ReactMixin)
reactMixin(Class.prototype, maybeMixin.mixin);
})
}
} As @gaearon pointed out, you can expect alternatives to mixins in the coming months... maybe before we get annotation support in babel. |
I think Babel already has annotation support behind a flag. |
@brigand I think the idea is to add decorators, not annotations to ES7. Class decorators are code so it's possible to have
I also think that decorator syntax will be useful for higher order components, so instead of:
we can write:
|
@brigand @andreypopp Here's a description of decorators that includes API and desugaring: https://github.com/wycats/javascript-decorators This is already implemented in Babel 5+ and TypeScript 1.5 |
This is not very sophisticated; it assumes the class does not define its own `shouldComponentUpdate`. For more discussion of the future for this kind of pattern, see: facebook/react#1380
Will we ever have automatic autobinding again? |
@jedwards1211 Unlikely. We'll probably recommend using a ES-future syntax feature when it lands: https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding. |
Hopefully the come up with something nice for that. More often than not, whenever I've passed functions around, I've wished they were autobound, even outside of React code. I understand why React devs don't "think you're in the business of designing a class system," but when you're going to start using an entire view framework, it's not that much cognitive burden to learn its class system. Certainly less cognitive burden for me than keeping track of all this churn. |
I guess this ship has sailed. |
Currently React's mixins allow multiple mixins to implement the same method in multiple mixin, if it's a whitelist. We would like to decouple the mixin system from React and therefore we need a way to solve it without a whitelist.
The idea is to have every mixin call super() to allow predictable chaining.
The mixin(...mixins) function would essentially create a new prototype chain with each mixin's set of functions stacked on top of each other in order. An object is treated as a prototype. A function is treated as a class/constructor and gets all their static methods + their prototype merged.
We can issue warnings when the mixin function is called and some of the overlapping methods are missing super calls.
Solvable but confusing/complex issues:
To be clear, mixins is an escape hatch to work around reusability limitations in the system. It's not idiomatic React.
Idiomatic React reusable code should primarily be implemented in terms of composition and not inheritance.
The text was updated successfully, but these errors were encountered: