Pull to refresh

className убивает ваш UI kit

Level of difficultyEasy
Reading time4 min
Views4.3K

UI kit играет важную роль в разработке веб-интерфейсов. Он представляет собой набор компонентов и стилей. Основная ценность его использования - это экономия времени. Вместо того, чтобы создавать каждый элемент с нуля, разработчики могут использовать уже готовые элементы, что ускоряет процесс разработки.

Гибкое API у UI kit компонентов является преимуществом. Однако возможность повлиять на стили ваших компонентов в конкретном месте является большим искушением. Разработчику выгодно внести изменение здесь и сейчас, а не думать на перспективу. Такое поведение постепенно разрушает вашу систему переиспользования.

Как же такое может быть?

По моим наблюдениям - это происходит по неопытности разработчика, или отсутствия у него ресурса разбираться с этим.

Например, дизайнеры и аналитики лоббируют компоненты, которые отличаются от тех, что есть в наборе. Разработчик под этим "давлением" начинает использовать "класс-костыль", чтобы как-то угодить требованиям.

Другой пример: Разработчик работает над одним из множества приложений своей компании. У него есть NPM-пакет с набором компонентов компании, который был создан ещё "отцами-основателями". При анализе дизайна, разработчик замечает, что есть расхождения с его реализацией. Тут образуется две опции для исправления:

A: Пройти через весь процесс внесения изменений в UI kit пакет
Б: Сделать "класс-костыль"

Вариант А требует больше энергии, чем Б. Поэтому вариант Б выглядят более привлекательнее.

Понятие "класс-костыль"

Это класс, который переопределяет стили для конкретного компонента в конкретном месте, вместо стилизации его глобально для всего приложения. Например, через тему.

Разбираемся на примерах

Для того, чтобы быть более предметным, рассмотрим примеры кода, где они отсортированы по их сложности имплементации.

Делаем c нуля (Сложно)

Возможная реализация

import React, { ButtonHTMLAttributes } from "react";

type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;

interface Props
  extends Pick<ButtonProps, "onClick" | "children" | "className"> {
  variant: "primary" | "secondary";
}

export const Button: React.FC<Props> = ({
  variant,
  className,
  ...buttonProps
}) => (
    <button
      {...buttonProps}
      className={["btn", variant ? `btn-${variant}` : null, className]
        .map(Boolean)
        .join(" ")}
    />
);

Использование

import { Button } from "@company/ui-kit";

export const App = () => (
  <div>
    <Button className="класс-костыль" variant="primary">
      Отправить
    </Button>
  </div>
);

Основываемся на готовом решении (Средне)

Возможная реализация

import React from "react";
import { Button, ButtonProps } from "antd";

interface Props
  extends Pick<ButtonProps, "onClick" | "className" | "icon"> {
  variant: "primary" | "secondary";
}
  
export const IconButton: React.FC<Props> = ({ variant, ...buttonProps }) => (
  <Button
    {...buttonProps}
    type={variant === "primary" ? "primary" : "default"}
  />
);

Использование

import { IconButton } from "@company/ui-kit";

const LikeIcon = () => <span>👍</span>;

export const App = () => (
  <div>
    <IconButton className="класс-костыль" icon={<LikeIcon />} />
  </div>
);

Используем напрямую готовое решение (Легко)

Тут нету примера реализации, поэтому переходим сразу к использованию

import { Checkbox } from "antd";

export const App = () => (
  <div>
    <Checkbox className="класс-костыль" checked />
  </div>
);

Влияние "класс-костыль" на наш UI kit

Из всего выше сказанного можно сделать вывод, что переопределение стилей в конкретном месте создаёт:

  • Усложнение поддержки: при обновлении версии библиотеки UI kit, могут возникнуть сложности с совмещением внесённых изменений и полученных обновлений.

  • Нарушение согласованности: потеря общей целостности дизайн системы из-за точечных изменений.

  • Риск появления ошибок: если переопределение стилей было выполнено неправильно. Например, не учтены какие-то сочетания классов.

  • Усложнение процесса разработки: анализ, как подкрутить стили, чтобы получить результат равный дизайну, отнимает время.

  • Увеличение кодовой базы: раздувается количество стилей.

Какие решения?

Можно заметить, что уровень сложности сопоставим с уровнем контроля реализации этого компонента.

Из этого примитивного графика, следуют следующие решения

Избавиться от className

В случае "делаем c нуля" и "основываемся на готовом решении" у нас достаточно контроля, чтобы не давать возможность определять className вовсе.

Конечно, если вы разрабатываете UI kit для переиспользования разными компаниями, как готовое решение, то в этом случае для вас предоставление возможности определения className - это фича, а не баг.

Для варианта "используем напрямую готовое решение" нельзя избавиться от определения className, так как отсутствует контроль реализации.

Добавить lint правило

Запрограммировать правило через написание собственного eslint плагина, который будет ограничивать использование класса для ваших UI kit компонентов. (Если такое возможно, подскажите кто знает в комментариях)

Попросить команду "так не делать"

Описать правило о запрете переопределения class в документации code style, на которую ссылаться во время code review.

Я не сторонник такого подхода, так как это не надёжно. Но для начала, определение проблемы - это уже половина решения.

Вывод

Точечное обновление стилей не является критической проблемой. Но если посмотреть на всю картину целиком, то это точно подтачивает преимущества использования UI kit. Сохранение баланса между дизайном и реализацией требует энергии и опыта. Думаю, что это не так просто, особенно если вы находитесь в одной из крайностей:

Вы Junior, у вас есть энергия, но нет опыта.
Вы Senior, есть опыт, но включен энергосберегающий режим.

Если вы ощущаете нарушение этого "баланса", то надеюсь, что мои аргументы добавят вам уверенности, чтобы сказать своей команде:

Tags:
Hubs:
Total votes 6: ↑6 and ↓0+6
Comments34

Articles