Simulate a Mixin in React ES6
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.