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.
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:
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.