
Catbee
Для чего? Что нового?
О костылях
This.signal('name')
this.$context.sendBroadcastAction('signal', { name, args, sync });
// Component.js (aka Morph)
// State.js (store) handleSignal (options) { this.signal(options.name, options.args, options.sync); }
signal (signal, args, sync = false) { var options = _.compact([sync, args]); this._controller.signals[signal](...options); // This is cerebral }
Почему сервер перегружается при изменении сигнала/экшена?
// Component.js (aka Morph)
class State { constructor () {
this
.resolveSignals();
}
resolveSignals () { _.chain(bulk(__dirname, ['../signals/*.js'])) .get('..') .get('signals') .map(this.resolveSignal, this) .run(); return this; } }
Почему сервер перегружается при изменении сигнала/экшена?
// Component.js (aka Morph)
class State { constructor () {
this
.resolveSignals();
}
resolveSignals () { _.chain(bulk(__dirname, ['../signals/*.js'])) .get('..') .get('signals') .map(this.resolveSignal, this) .run(); return this; } }
Как сервер дожидается окончания сигнала?
// Document component
render () { return this.$context.sendBroadcastAction('getRestorePromise') .then((data) => this.restoreContext(data)); } // State store
handleGetRestorePromise () { return this._stateRestorePromise .then(() => this._tree.serialize()); } // Тут вообще все весело :)
restore () { if (!this.$context.isServer) { this._stateRestorePromise = new Promise(_.bind(this._restoreClientTracker, this)); return this._stateRestorePromise; } this.runRouterSignal(); this._stateRestorePromise = new Promise(_.bind(this._restoreSignalTracker, this)); return this._stateRestorePromise; }
Как сервер дожидается окончания сигнала?
// Document component
render () { return this.$context.sendBroadcastAction('getRestorePromise') .then((data) => this.restoreContext(data)); } // State store
handleGetRestorePromise () { return this._stateRestorePromise .then(() => this._tree.serialize()); } // Тут вообще все весело :)
restore () { if (!this.$context.isServer) { this._stateRestorePromise = new Promise(_.bind(this._restoreClientTracker, this)); return this._stateRestorePromise; } this.runRouterSignal(); this._stateRestorePromise = new Promise(_.bind(this._restoreSignalTracker, this)); return this._stateRestorePromise; }
Что такое /orgs/:orgId[state]?
// State store runRouterSignal () { var state = this.$context.state; var signal = _.get(state, 'signal'); var args = _.omit(state, 'signal'); if (!signal) { throw new Error(`Current route missing signal`); } this.signal(signal, args); }
Кто такой Watcher?!
constructor (selection = {}, parse = utils.noop) { this._safeWatcherPromise = this.$context.sendAction('state', 'select', selection) .then((watcher) => this._watcher = watcher) .then(_.bind(this._watch, this)); this._parse = parse; }
load () { return this._safeWatcherPromise .then(() => this._watcher.get()) .then((data) => this._parse(data)); }
_watch () { if (this.$context.isServer) { return; } this.$context.sendBroadcastAction('getRestorePromise') .then(() => this._watcher.on('update', this.$context.changed)); }
Что еще плохо?
return _.chain(data) .thru(_.partial(Store.setType, campaign)) .thru(_.partial(Store.setStatus, campaign)) .thru(_.partial(Store.setStatistics, campaign)) .thru(_.partial(Store.setOrder, campaign)) .thru(_.partial(Store.setBudget, campaign)) .thru(_.partial(Store.preparePositions, campaign)) .thru(_.partial(Store.preparePermissions, campaign)) .value();
Вызывается N раз, где N это число компонентов которым нужны эти данные
Как решить?
orderStartDate ({ id, state }) {
state.set(['auction', 'computed', id, 'order', 'start'], monkey([
['auction', 'campaigns', { id }, 'order', 'startDate'],
['auction', 'campaigns', { id }, 'order', 'endDate'],
(startDate, endDate) => {
if (!startDate || !endDate) {
return;
}
return date.beautyStartDate(startDate, endDate);
}
]));
},
Решаем ленью!

Что еще плохо?
Do more one thing
/** * Устанавливаем информацию о заказе * @param {Object} campaign * @param {Object} data * @returns {Object} */ static setOrder (campaign, data) { var order = _.get(campaign, 'order'); if (!order) { return data; } var { orderFormNumber, startDate, endDate } = order; data.order = { orderFormNumber, startDate: Store.beautyStartDate(startDate, endDate), startDateFull: startDate && moment.utc(startDate).format('DD.MM.YYYY'), endDate: endDate && moment.utc(endDate).format('DD.MM'), endDateFull: endDate && moment.utc(endDate).format('DD.MM.YYYY') }; return data; }
Пиши нормально!!!

Ты не поверишь! Но это шаблон компонента!
<div class="auctionCardHead__order"> <div class="auctionCardHead__orderHolder"> <span class="auctionCardHead__orderStatus _{{status}}"></span> <cat-tooltip id="tooltip-status-{{id}}" class="tooltip _noWrap _isSmall" {{#if store}}cat-store="{{store}}"{{/if}} cat-action-args="{{id}}" cat-middle="true" cat-event="getStatusTooltip" cat-orientation="topToBottom"> </cat-tooltip> </div> <div class="auctionCardHead__orderTitle"> <div class="auctionCardHead__orderTitleName _{{status}}"> Рекламная кампания </div> <div class="auctionCardHead__orderTitleLink _{{status}} {{#unless isMFH}}_isCPS{{/unless}}" cat-status="{{status}}"> {{ order.orderFormNumber }} </div> </div> <div class="auctionCardHead__orderDate"> от {{ order.startDateFull }} </div> </div> <div class="auctionCardHead__statistic"> {{#if isMFH}} {{#if permissions.isStatisticsEnabled}} <div class="auctionCardHead__statisticItem"> <div class="auctionCardHead__statisticItemNumber"> {{ statistics.periodClicksCount }} </div> <div class="auctionCardHead__statisticItemDescription"> {{ statistics.paidClicksSubtitle }}<br/> с {{ statistics.periodStartDate }} по {{ statistics.periodEndDate }} </div> {{#if permissions.isAuctionEnabled}} <a href="" class="auctionCardHead__statisticLink">Смотреть детальную статистику</a> {{/if}} </div> <div class="auctionCardHead__statisticItem"> <div class="auctionCardHead__statisticItemNumber _isPrice"> {{ statistics.averageClickPrice }} </div> <div class="auctionCardHead__statisticItemDescription"> средняя стоимость клика<br/> с {{ statistics.periodStartDate }} по {{ statistics.periodEndDate }} </div> </div> {{/if}} <div class="auctionCardHead__statisticItem"> {{#if budget}} <div class="auctionCardHead__statisticItemNumber _isPrice">{{ budget.remaining }}</div> <span class="auctionCardHead__statisticTotal"> из {{ budget.monthly }} <span class="auctionCardHead__rouble"></span> </span> <div class="auctionCardHead__statisticItemDescription"> остаток ежемесячного <br/>бюджета </div> {{#if permissions.isBudgetIncreaseEnabled}} <a href="#" class="auctionCardHead__statisticLink _refill">Увеличить бюджет</a> {{/if}} {{/if}} </div> {{else}} <div class="auctionCardHead__positions"> {{#each positions.items}} <div class="auctionCardHead__positionsItem">{{ name }}</div> {{/each}} {{#if positions.meta.count}} <div class="auctionCardHead__positionsItem"> <a href="" class="link link_type_local auctionCardHead__positionsMore"> {{positions.meta.count}} <div class="auctionCardHead__tooltip"> <cat-tooltip id="tooltip-positions-{{id}}" class="tooltip _isSmall _leftOriented" {{#if store}}cat-store="{{store}}"{{/if}} cat-action-args="{{id}},positions" cat-event="getHiddenTooltip" cat-width="242px" cat-orientation="topToBottom"> </cat-tooltip> </div> </a> </div> {{/if}} </div> {{/if}} </div>

Как решить проблему?
Декомпозируй


VS
Что еще плохо?!
var selection = { validation: ['commercials', 'validation'], editors: ['commercials', 'items'], scheme: ['commercials', 'scheme'], currentCardId: ['commercials', 'currentCardId'], currentCommentId: ['commercials', 'currentCommentId'], currentOrder: ['auction', 'currentOrder'], orders: ['auction', 'orders'], orgs: ['user', 'orgs'], orgId: ['orgId'], status: ['commercials', 'status'], isSyncing: ['commercials', 'isSyncing'], changes: ['commercials', 'changes'], address: ['branches', 0, 'address'], isPreviewVisible: ['commercials', 'UIState', 'isPreviewVisible'], isCleanupModalVisible: ['commercials', 'UIState', 'isCleanupModalVisible'], permissions: ['permissions', 'adverts', 'items'] };
Проодлжение следует...
Что еще плохо?!
handleGetDenialList () { return this._getData() .then(Store.getDenialList); } handleGetSelectItems () { return this._getData() .then(Store.getSelectItems); } handleGetWidgetData () { return this._getData() .then((data) => { return { previews: _.chain(data.editors) .groupBy('type') .keys() .map((type) => Store.typeMap[type]) .sortBy((type) => Store.editorPriority[type]) .value() }; }); } handleGetQuestionTooltip (type) { var content = Store.tooltips[type]; return { content }; }
Проодлжение следует...
Что еще плохо?!
handleGetCardData () { return this._getData() .then(_.partial( Store.getEntityData, _.partial(Store.prepareEditor, 'currentCardId', 'openCardComment'), 'openCard' )); } handleGetCommentData () { return this._getData() .then(_.partial( Store.getEntityData, _.partial(Store.prepareEditor, 'currentCommentId', 'closedCardComment'), 'rubricComment' )); } handleGetCPSData (type) { return this._getData() .then(_.partial(Store.getCPSData, type)); }
Как решить?
Разделить это на 2 части
Watch
module.exports = (attributes) => {
var id = attributes['cat-id'];
return {
orderId: ['auction', 'campaigns', { id }, 'order', 'orderFormNumber'],
start: ['auction', 'computed', id, 'order', 'extended', 'start'],
isMFH: ['auction', 'computed', id, 'isMFH'],
status: ['auction', 'computed', id, 'status']
}
};
module.exports = { currentOrder: ['auction', 'currentOrder'], orgId: ['orgId'] };
Как решить?
Разделить это на 2 части
Prepare
// Component level
render () {
return this.$context.getWatcherData().then(function(data) {
// Prepare data here
return data;
});
}
// State level
setActiveCampaignsFilter ({ filter = 'all' }, state) {
state.set(['auction', 'filter'], filter);
}
Каждая смена URL запускает сигнал страницы! Беда беда!
this.context.redirect('/?filter=true', { silent: true });
Нужно быть осторожнее!
CSS Modules

deck
By Kirill Kaysarov
deck
- 958