ReactJs - Using Mixins with ES6
Continued on video Egghead.io - "React Components in ES 6 classes", below works:
'use strict';
import React from 'react';
import Button from './components/form/button';
import Label from './components/form/label';
// after refactoring
//import ReactMixin from './super-classes/react-mixin';
// ToDo: Reactor below
let reactMixin = InnerComponent => class extends React.Component {
constructor() {
super()
this.state = {count: 0}
//binding custom methods
this.increment = this.increment.bind(this);
}
increment() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<InnerComponent update={this.increment} {...this.state} {...this.props} />
)
}
}
let ButtonMixed = reactMixin(Button); // using local variable
let LabelMixed = reactMixin(Label); // using local variable
class App extends React.Component {
render(){
return(<section>
// WOULD LIKE TO DO
// <ReactMixin component={?Button?} />
// <ReactMixin component={?Label?} />
<LabelMixed txt="I am a mixed label calling a form component" />
<ButtonMixed txt="I am a mixed button, me too! " />
</section>);
}
}
App.propTypes = {txt: React.PropTypes.any};
module.exports = App;
Problem:
I am trying to refactor ReactMixins into a separate component, import it and then just use it in my render, like this:
<ReactMixins component={?} />
Any ideas on the best way to refactor this for reusability?
Thank...
Update: After working with LOT more with ES6 React components, I'm much more into the compositional approach, but I'll leave my answer here for posterity.
Mixins come out, prefer composition or inheritance instead.
If I understand your situation correctly, it is easiest to create a "base" component that you inherit. Something like:
export default class BaseButton extends React.Component {
constructor(){
super();
//Set default state... if you want to
}
componentWillMount() {
console.log("I will run anytime someone extends baseButton");
}
//etc
}
where all your Button logic is located, you can extend it like this
Then:
export default class MyButton extends BaseButton {
//I will already have the things that BaseButton has
}
now you have whatever you want through super()
or this.whatever()
.
If you prefer a compositional approach, I recommend this as a good one:
https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
source to share
My solution is for ES6 mixins if you are using decorators.
/**
* This is the ES6 mixin helper
*
* mixins {Array} Array of mixin objects
* mixin:
* optional name: {String} mixin name
* optional lifecycleMethods: {Object} lifecycle methods
* optional instances: {Object} instance methods
* optional statics: {Object} statics methods
* component {Object} Abstract component
*
* return component {Object} mixed component
*/
export default function ES6Mixin (mixins) {
return function (component) {
const warn = function (type, method, mixinName) {
console.warn(
new component(),
`already has \`${method}\` ${type} method, it overwritten by \`${mixinName}\` mixin`
);
};
mixins.forEach((mixin) => {
const { name, statics, instances, lifecycleMethods } = mixin;
const _componentWillMount = component.prototype.componentWillMount;
if (statics instanceof Object) {
Object.keys(statics).forEach((method) => {
if (statics[method] instanceof Object) {
component[method] = Object.assign({}, statics[method], component[method]);
} else {
if (this[method]) {
warn('statics', method, name || 'anonym');
}
this[method] = statics[method];
}
});
}
if (instances instanceof Object) {
component.prototype.componentWillMount = function () {
Object.keys(instances).forEach((method) => {
if (this[method]) {
warn('instance', method, name || 'anonym');
}
this[method] = instances[method].bind(this);
});
if (_componentWillMount) {
_componentWillMount.apply(this, arguments);
}
};
}
if (lifecycleMethods instanceof Object) {
Object.keys(lifecycleMethods).forEach((method) => {
let _lifecycleMethod = component.prototype[method];
component.prototype[method] = function () {
lifecycleMethods[method].apply(this, arguments);
if (_lifecycleMethod) {
_lifecycleMethod.apply(this, arguments);
}
};
});
}
});
return component;
};
}
source to share