Используем React: эксклюзивная лечебная методика сидения на трёх стульях сразу
Invite pending
Где болит
Как давно подмечено — легко в теории, хаос на практике. Несмотря на использование архитектурных шаблонов, типа Flux, Reflux, Redux, реальная жизнь (если только задача вашего кода несколько больше, чем «Hello World!») гарантирует ситуацию, когда код ваших React-компонентов может вырасти до размеров, несовместимых с лёгкостью чтения и осмысления. Как часто среди переопределённых методов жизненного цикла громоздятся либо дополнительные функции обработчиков разнообразных кликов, либо вспомогательная функциональность, решающая что рендерить в том или ином случае, либо всё сразу и.д. и т.п. И вот если Вы достигли данного состояния сложности бытия, то возникает закономерный вопрос: как отрефакторить всю получившуюся кучу гов..., простите, кода, в нечто приятное глазу и пониманию.
Как лечить
На основе личной практики предлагаю модель расчленёнки: Model-Component-View (MCV). Вашему вниманию предлагается принцип разделения кода компонента на три части согласно взгляда на стандартный реакт-компонент в трёх аспектах: данные, инфраструктура самого реакта, пользовательский интерфейс. В результате такой формализации из одного реакт-компонента получим три модуля (класса):
- Model — подобна ModelView из паттерна MVVM — сюда мы выносим код определяющий отношения нашего компонента с окружающим его миром — прежде всего логика отображения данных (в этом модуле возможна завязка на архитектурные подходы типа Flux).
- Component — собственно компонент (такой весь зачатый от React.Component) с его сложным жизненным циклом и жизненным путём и даже с рендером..., но без разметки. А вот о ней заботится следующий элемент:
- View — здесь собственно разметка, ну и задача данного модуля — представление (и только представление!) данных получаемых от модели.
Особо хочу подчеркнуть, что данный подход никак не конфликтует с известными архитектурными подходами используемыми в React, поскольку лежит вне их пределов — он о другом. Он о том, по каким принципам и как производить декомпозицию самих компонентов (для чего — смотри главу «где болит»).
Применение и дозировка
В случае небольшого кол-ва переопределённых методов жизненного цикла, наличия одного обработчика onClick и куцей разметки применять НЕ РЕКОМЕНДУЕТСЯ.
Терапевтический эффект
Улучшение стуктурированности кода на фоне декомпозиции.
Анатомический театр
Ну и не как рецепт, а в качестве руководства к действию немножко демонстрации предлагаемого подхода (пример, естественно, упрощённый):
//Собственно компонент - здесь только стадии жизненного цикла и ничего более.
class Order extends React.Component{
constructor(props){
super(props);
this.state = {orderStruct: props.orderData};
}
componentWillReceiveProps(nextProps){
this.setState({orderStruct: (nextProps).orderData});
}
render(){
new OrderModel(this);
new OrderView(this.model);
return this.view.render();
}
}
//Модель отображения - здесь мы определяем, что и как должно заполнять нашу разметку
class OrderModel{
constructor(component){
//устанавливаем в компоненте ссылку на модель
component.model = this;
this.component = component;
}
get orderStruct(){
return this.component.state.orderStruct;
}
//Свойства и методы для отображения атрибутов заказа ------------
get orderTitle(){
try{
return 'Заказ на обслуживание № ' + this.orderStruct.orderNumber;
}
catch{
return 'Данные отсутствуют';
}
}
...............................
//Обработчики событий и т.п. ---------------
handleResetStatusButtonClick(){
//Обращение к гипотетическому диспетчеру (в рамках данного примера не рассматривается)
//для вызова действия обновления статуса заказа
var dispatcher = new Dispatcher();
dispatcher.callAction('ResetOrderStatus', this.component.refs.statusCtrl.status);
}
}
//Представление - только вывод.
class OrderView{
constructor(model){
this.model = model;
}
render(){
return <div>
<OrderTitle caption={this.model.orderTitle}>
.......................
.......................
.......................
.......................
.......................
<OrderStatusControl status={this.model.status} ref="statusCtrl" />
<ResetStatusButton caption="Обновить статус" onClick={this.model.handleResetStatusButtonClick} />
</div>
}
}