Вѝдение ES6+React+BEM

Update: в React БЭМ не нужен: функциональная композиция, DI, css-modules (или, менее приоритетно, classnames или inline-styles в некоторых случаях), импорты и пр. компоненты развитой инфраструктуры не оставляют БЭМу ни крошки хлеба: ни в именовании, ни в js-логике(об этом в этой частично устаревшей статье ниже), ни в разрешении зависимостей. В reBEM с этим не согласны, говоря о проблемах с каскадом в css-modules, которых я не понимаю и приглашаю к дискуссии.


Не отражает официальную позицию команды Яндекса.

Под БЭМ тут понимается и именование классов, и шаблонизация, а так же клиентская логика, на которой я сфокусируюсь в этой заметке.

`inherit.js` не нужен и без него легко обойтись

Я просмотрел декларации БЭМ-блоков под модификаторами которые нашёл. И абсолютное большинство модификаторов в `BEM.DOM.decl({block,modName,modVal})` являются статическими.

Пояснение: в БЭМ нет разделения модификаторов на динамические и статические (иными словами на модификаторы и состояния), как в вариантах `БЭМ*`, `БЭМС`. Некоторые модификаторы ставятся для конкретного экземпляра «на всегда» (`theme`, например), а другие динамичекси ставятся и снимаются постоянно (`focused`).

Пара динамических модификаторов (состояний) встретилась.

Логика под статическими модификаторами

… легко переписывается без `inherit.js` на `class ButtonThemeNormal extends Button {}` и даже если эту тему захочется динамически менять — это редкий случай и добиться этого можно 1) заменой блока в html или 2) переключением видимости между двумя экземплярами блока.

Декларации логики под динамическими модификаторами

… заменяется с помощью `@декораторов` из `es7`, которые 1) записываются декларативно: вместо явных `if` в начале методов, которые дожлны срабатывать только под модификаторами 2) избавляют от `inherit.js` для обычных методов и лишь для методов под модификаторами добавят обёртку (перед этими методами записывается `@mod(‘clear-shown’))` 3) декораторы уже работают благодаря babel (в последней версии нужно включать legacy-плагин, т.к. стандарт ещё не устоялся и реализация выпилена из коробки).

Преимущества отказа от `inherit.js`: 1) callstack становится читаемым для людей 2) можно водрузить БЭМ на `es6+` и `react` прямее, с возможностью пользоватся классами, встроенным наследованием (extends), супер-вызовами (super)

Upd О миксинах и наследовании как паттернах проектирования

Безусловно, наследование не натягивается на миксины (не путать с БЭМ-миксами). Наследование не должно применяться как единственный способ получения модифицированного блока. Вот пример, как это выглядит. Импортируем блок button из libs/bem/desktop, который уже наследует libs/bem/common (для уровней доопределений под платформы common,desktop,touch-phone и тд. наследование подходит идеально), а затем в коде блока в проекте создаём ButtonNormalS для удобства. Но если потребуются и другие модификации кнопки, то мы передадим нужные модификаторы в функцию рендеренга:

Отказ от состояния в библиотечных блоках

… раскрывает возможности React. Избавляет от проблем с синхронизацией состояний, выносит состояние в вашу логику из компонент, что способствует разделению логики и представления.
Этот отказ выражается, например, в избавлении от методов `getText`, `setText` и тому подобных.

Upd про логику и состояние библиотечных блоков

Более сложные чем кнопка библиотечные блоки не могут быть только лишь представлением, им нужна логика и состояние в том или ином виде. В соответствии с концепцией «Presentational and Container Component» описанной Деном Абрамовым, библиотеные компоненты поставляются в отдельных ипостасиях. Вот так это условно выглядит: `lib/components/dropdown.jsx`, `lib/actions/dropdown.js`, `lib/reducers/dropdown.js`. Экшены и редьюсеры соединяются с оными в приложении.

Пример с чекбоксом (осторожно, спорно)

Во всех UI-системах есть чекбокс. И во всех известных мне реализациях этот чекбокс имеет собственное состояния. Революция в отказе от его состония. На первый взгялд кажется диким, что чекбок перестанет переключаться самостоятельно, а будет лишь отображать переданное в него значение `checked` и дергать хендлер `onClick`. Тогда рассмотрим пример: есть чекбокс и хочется, чтобы он отражал настоящее состояние контролируемого им значения, но добиться этого можно только костылём: автоматически возвращать чекбокс в выключенное состояние сразу по включению и «включать» когда например ядерный реактор им управляемый включился. Но как только чекбокс перестаёт самостоятельно менять состояние по клику, мы контролируем его сами. 1) По клику отправляем сигнал запуска реактора и дисейблим чекбокс 2) по изменении состояния реактора в чекбокс устанавливается новое значение `checked` и он переключается 3) снимаем дисейбл.

БЭМ-события и хендлеры onSetMod

… заменяются конечным автоматом, с экшенами и состояниями который Redux стремительным домкратом привносит во фронтенд.


Здесь было кратко рассмотрено, как должен строиться клиентский БЭМ-рантайм: а именно, от него ничего не остаётся.
В следующих заметках рассмотрим 1) БЭМ-именование классов в React 2) БЭМ-шаблонизацию в React и другие аспекты сочетания ежа и носорога. tl;dr spoiler: сохраняется jsx, добавляются props с БЭМ-смыслом, с помощью хелперов и babel-трансформации, формирующих css. Такая функциональность уже доступна в некоторых npm-пакетах. И далее я расскажу, какие из них вписываются в моё вѝдение bem+react, а какие — нет.