user1177440
user1177440

Reputation:

ReactJs - Using Mixins with ES6

Following up on Egghead.io's video - 'React Components in ES 6 classes', the 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 out ReactMixins to a seperate component, import it in, and then just use it in my render, like this:

          <ReactMixins component={?} />

Any ideas on how best to refactor this for multiple usage?

Thanks ...

Upvotes: 4

Views: 6705

Answers (2)

Jonathan Rowny
Jonathan Rowny

Reputation: 7588

Update: After working a LOT more with ES6 React components, I'm much more in favor of the composition approach, but I'll leave my answer here for posterity.

Mixins are on their way out, instead favor composition or inheritance.

If I understand your situation correctly, the easiest thing to do is create a "base" component that you inherit from. 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, then you can extend it like so

Then:

export default class MyButton extends BaseButton {
   //I will already have the things that BaseButton has
}

now you've got everything you want available via super() or this.whatever().

If you favor a composition approach, I recommend this as a good read:

https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Upvotes: 6

Peter Molnar
Peter Molnar

Reputation: 11

My solution for ES6 mixins if you use decorators.

/**
 * This is the ES6 mixin helper
 *
 * mixins {Array} Array of mixin objects
 *  mixin:
 *    optional name: {String} mixin's 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's 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;
  };
}

Upvotes: 1

Related Questions