Я хочу написать коротенький пост, про то, как я решил проблему наследования в ReactJS. Обычно, на форумах, люди советуют использовать миксины для наследования функционала, но, по-моему, это не совсем правильно. Все-таки трэйты/миксины и классы это не одно и то же, да еще и из-за возможности множественного наследования могут возникать вот такие казусы:
var A = {
doStuff (){}
}
var B = {
doStuff (){}
}
var C = React.createClass({
mixins: [A, B]
});
//упс... ошибка, потому что React не может решить какой из doStuff унаследовать
К тому же, миксины не позволяют делать стандартные ООПешные фишки вроде перезаписи методов(method override):
var A = {
doStuff (){}
}
var C = React.createClass({
mixins: [A],
doStuff (){
//неа, не получится
}
});
А без этого, естественно, не сработает и расширение функционала как во «взрослых» ООП языках:
doStuff (){
super.doStuff()
//дополнительный функционал
}
Конечно, классы ES6 решат эту проблему, и команда ReactJS к этому готовится, если судить по постам на их блоге, но ждать ES6 придется как второго пришествия, а затем, придется еще подождать пока не вымрут старые Интернет Эксплореры без поддержки ES6.
Итак, я хочу предложить вам альтернативный метод, который и сам использую, но для этого вам понадобятся:
1) Система модулей/зависимостей: RequireJS/Browserify/WebPack/что там еще сейчас в моде. Если вы не пользуетесь/не знаете что такое JavaScript модули, что ж, самое время узнать.
2) Какая-нибудь функция/либа для глубокого копирования объектов, например, jQuery.extend, _.extend и т.п.
Итак, я пишу модули своих компонент следующим образом:
var React = require('react');
var Human = {
whoAreYou (){
return "I'm a human";
}
whatDoYouDo (){
return "I'm just chilling";
}
render (){
return (
<h1>{this.whoAreYou()}<small>{this.whatDoYouDo()}</small></h1>
)
}
}
module.exports = {
Class: Human,
Component: React.createClass(Human)
}
Фишка в том, что я экспортирую не только компоненту, но и «чистый» объект, из которого эта компонента создается, таким образом, когда мне надо использовать просто компоненту <Human/>, я беру поле Component из экспорта моего модуля:
var Human = require('human').Component;
А вот когда мне надо от моего модуля унаследовать, и тут начинается самое интересное, я использую поле Class:
var React = require('react');
var Parent = require('human').Class;
var Programmer = {};
jQuery.extend(true, Programmer, Parent, {
whoAreYou (){
return Parent.whoAreYou.apply(this) + " and a programmer";//вызов метода из родителя!
}
whatDoYouDo (){
//перезапись метода полностью
return "I write code";
}
drinkCoffee (){
//добавление нового метода
console.log('*sip*');
}
});
Естественно, этот модуль я тоже экспортирую согласно вышеописанной «конвенции»:
module.exports = {
Class: Programmer,
Component: React.createClass(Programmer)
}
И теперь его можно использовать в приложении:
var Programmer = require('programmer').Component;
Ну, или наследовать/расширять дальше, например, в JuniorDeveloper.
И на этом все, это был мой коротенький пост про костыль для (псевдо)наследования в ReactJS. Успешной вам трудовой недели, господа!
Автор: diogene