After switching a few of my projects over to React ES6, the issue of the lack of mixins is a recurring problem. Egor Smirnov offers a few alternatives on his blog. And I understand the message of composition, I very much agree for many cases. But for a few purposes, there is a need for the mixin-functionality. One such case in my projects is for components that demand localization.

To simulate a mixin, I implemented a simple function that takes a React class and modifies it by replacing the functions I need with new versions, it then calls them after doing it’s own logic. This is very similar to what mixins did before.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function TranslatedComponent(component) {

  let didMount = component.prototype.componentDidMount;
  let willUnmount = component.prototype.componentWillUnmount;

  function _translatedComponent_handleLanguageChanged() {
    // We bypass shouldComponentUpdate here
    // This is part of the reason we can't use composition
    this.forceUpdate();
  }

  component.prototype.componentDidMount = function() {
    // Store reference to the listener so we can unregister
    this._translatedComponent_onLanguageChanged = _translatedComponent_handleLanguageChanged.bind(this);

    // Listen to language changes
    TranslationStore.addChangeListener(this._translatedComponent_onLanguageChanged);

    // Forward event
    if (didMount) {
      didMount.apply(this);
    }
  }

  component.prototype.componentWillUnmount = function() {
    // Unsubscribe & clean up listener
    TranslationStore.removeChangeListener(this._translatedComponent_onLanguageChanged);
    this._translatedComponent_onLanguageChanged = null;

    // Forward event
    if (willUnmount) {
      willUnmount.apply(this);
    }
  }

  // Convenience function
  component.prototype.translate = function() {
    return TranslationStore.get.apply(TranslationStore, arguments);
  }

  // We don't return anything to stress the fact that we mutate
  // the class passed
}

This allows you to augment the class in any way you like. You use the “mixin” by calling the mixin with the class you want to augment as an argument:

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';
import TranslatedComponent from '../utils/TranslatedComponent.js';

class FancyComponent extends React.Component {
  // Stuff...
}

// Returns nothing because it mutates the class
TranslatedComponent(FancyComponent);

// Export as normal
export default FancyComponent;

This is a very simple solution to the problem, and it works well for the cases when composition simply won’t do the job.