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