Мышление «сначала утилиты» — Примеры

Пример 1 — Один и тот же список классов в четырёх местах

Список классов панели инструментов скопирован в четыре шаблона, так что теперь для изменения отступа нужны четыре правки, — а одну из них в прошлый раз пропустили. Оцените, чего стоит повторение, и скажите, где место этой абстракции, — а затем исправьте.

<div class="flex items-center gap-2 rounded-md border px-3 py-1.5">
  <span class="text-sm font-medium">Заказы</span>
</div>

<!-- те же двенадцать классов повторены ещё в трёх шаблонах -->

Решение

export function Toolbar({ children }) {
  return (
    <div className="flex items-center gap-2 rounded-md border px-3 py-1.5">
      {children}
    </div>
  );
}
<Toolbar><span className="text-sm font-medium">Заказы</span></Toolbar>
<Toolbar><span className="text-sm font-medium">Счета</span></Toolbar>

Когда список утилит повторяется, абстракция здесь — компонент, а не новый семантический класс. Изобретая класс, вы возвращаетесь к проблемам именования и удаления, от которых ушли, но инструментов у вас теперь меньше. Компонент хранит список в одном файле, носит имя, которое можно произнести вслух, и каждое место вызова передаёт только отличающееся. Утилиты делают оформление читаемым там, где оно применяется, — и как раз граница компонента не даёт этой читаемости превратиться в шаблон, который никто не станет проверять. Выделять стоит на втором повторении, а не на десятом.

Пример 2 — Имена классов, собранные из строк

Помощник выбирает размер текста, склеивая префикс с переменной, и в разработке всё работает, а в собранном выводе стилей нет. Объясните, почему сборка их не видит, и напишите классы так, чтобы они выжили.

function Label({ size = "sm", children }) {
  return <span className={"text-" + size}>{children}</span>;   // text-sm так и не сгенерируется
}

Решение

const sizeClass = { sm: "text-sm", md: "text-base", lg: "text-lg" };

function Label({ size = "sm", children }) {
  return <span className={sizeClass[size]}>{children}</span>;
}

Сборка обходит исходники в поисках буквальных имён классов и генерирует только найденные правила, поэтому имя, собранное во время выполнения, ничему не соответствует и правило не создаётся. Отсюда и разница: в разработке стиль работает, ведь там ничего не фильтруется, а в продакшене пропадает. Если держать каждый возможный класс буквально в объекте-словаре, сканер найдёт три настоящих имени, а место вызова не изменится. Альтернатива — список исключений в конфигурации — работает, но незаметно растёт, и следующий компонент, который склеивает имя из строк, наследует тот же невидимый отказ.