Code review чеклисты

В проектах на JavaScript и TypeScript, использующих Radix UI, код-ревью выполняет не только традиционные задачи проверки качества кода, но и контролирует правильность использования примитивов библиотеки: доступность компонентов, корректную композицию, управление состоянием и соблюдение архитектурных принципов.

Radix UI предоставляет низкоуровневые, доступные UI-примитивы, поэтому большая часть логики поведения и визуального слоя реализуется разработчиком. Именно по этой причине наличие формализованных чеклистов code review критически важно: они позволяют системно проверять корректность интеграции компонентов и предотвращать накопление технического долга.

Чеклист code review для Radix UI обычно включает следующие категории:

  • архитектура компонентов
  • корректность использования примитивов
  • доступность (accessibility)
  • управление состоянием
  • композиция компонентов
  • производительность
  • стилизация
  • обработка событий
  • тестируемость
  • безопасность и устойчивость интерфейса

Проверка архитектуры компонентов

Изоляция UI-логики

Компоненты Radix UI должны использоваться как поведенческие примитивы, а не как контейнеры бизнес-логики.

Проверяется:

  • разделение UI и бизнес-логики
  • отсутствие API-запросов внутри компонентов Radix
  • отсутствие сложной бизнес-логики в обработчиках UI

Неправильный подход:

const UserMenu = () => {
  const [user, setUser] = useState(null)

  useEffect(() => {
    fetch("/api/user")
      .then(res => res.json())
      .then(setUser)
  }, [])

  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger>
        {user?.name}
      </DropdownMenu.Trigger>
    </DropdownMenu.Root>
  )
}

Проблема:

  • компонент UI содержит сетевую логику
  • нарушается принцип разделения ответственности

Правильная архитектура:

const UserMenu = ({ user }) => {
  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger>
        {user.name}
      </DropdownMenu.Trigger>
    </DropdownMenu.Root>
  )
}

Получение данных переносится в контейнерный компонент.


Композиционная структура

Radix UI построен на принципе композиции примитивов. Чеклист должен проверять, что структура компонентов соответствует рекомендованной иерархии.

Например, для DropdownMenu:

Правильная структура:

<DropdownMenu.Root>
  <DropdownMenu.Trigger />
  <DropdownMenu.Content>
    <DropdownMenu.Item />
  </DropdownMenu.Content>
</DropdownMenu.Root>

Во время code review проверяется:

  • наличие обязательных элементов
  • корректное вложение
  • отсутствие нарушений иерархии

Ошибки композиции часто приводят к:

  • неработающей навигации клавиатурой
  • некорректной доступности
  • проблемам с фокусом

Проверка корректности использования примитивов

Использование Root компонентов

Каждый Radix-компонент имеет Root, управляющий состоянием.

Пример:

Dialog.Root
Popover.Root
DropdownMenu.Root

Проверяется:

  • используется ли Root
  • не создаются ли лишние Root-контейнеры
  • корректно ли управляется состояние

Проблема:

<Dialog.Trigger>
  Open
</Dialog.Trigger>

Без Dialog.Root компонент работать не будет.


Контролируемые и неконтролируемые компоненты

Radix поддерживает два режима управления состоянием:

Неконтролируемый

<Dialog.Root defaultOpen={false}>

Контролируемый

<Dialog.Root open={open} onOpenCha nge={setOpen}>

Во время code review проверяется:

  • не смешиваются ли режимы
  • корректно ли обрабатывается onOpenChange
  • не возникает ли рассинхронизации состояния

Плохая практика:

<Dialog.Root open={open} defaultOpen={false}>

Такое использование создаёт конфликт состояния.


Проверка доступности (Accessibility)

Одно из главных преимуществ Radix UI — встроенная поддержка accessibility. Однако неправильное использование компонентов может её нарушить.

Проверка ролей и aria-атрибутов

Radix автоматически устанавливает:

  • ARIA roles
  • aria-attributes
  • keyboard navigation

Code review должен проверять, что эти механизмы не были сломаны кастомизацией.

Например:

Плохой код:

<Dialog.Trigger asChild>
  <div>Open</div>
</Dialog.Trigger>

Проблема:

  • div не является интерактивным элементом

Правильно:

<Dialog.Trigger asChild>
  <button>Open</button>
</Dialog.Trigger>

Проверка навигации клавиатурой

Radix обеспечивает поддержку:

  • Tab
  • Arrow keys
  • Escape
  • Enter

Во время code review проверяется:

  • не блокируются ли события клавиатуры
  • не перехватываются ли key events без необходимости

Плохой пример:

onKeyD own={(e) => e.preventDefault()}

Это может нарушить встроенную навигацию.


Управление фокусом

Radix автоматически управляет фокусом:

  • фокус при открытии
  • возврат фокуса при закрытии
  • focus trap

Проверяется:

  • не отключён ли focus trap
  • нет ли принудительного изменения фокуса

Плохой пример:

useEffect(() => {
  document.querySelector("input")?.focus()
})

Это может конфликтовать с Radix.


Проверка композиции компонентов

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

Radix поддерживает замену DOM-элементов через asChild.

Пример:

<Dialog.Trigger asChild>
  <Button />
</Dialog.Trigger>

Code review должен проверять:

  • поддерживает ли переданный компонент ref
  • прокидываются ли props

Проблемный компонент:

const Button = ({ children }) => {
  return <button>{children}</button>
}

Он не принимает ref.

Правильный вариант:

const Button = forwardRef((props, ref) => (
  <button ref={ref} {...props} />
))

Проверка управления состоянием

Синхронизация состояния

Radix компоненты часто управляют состоянием:

  • открытие
  • выбор элементов
  • переключение

Важно проверять:

  • не возникает ли двойного источника состояния
  • не используется ли лишний useState

Плохой пример:

const [open, setOpen] = useState(false)

<Dialog.Root>

Но состояние Root уже управляется внутри.


Избежание избыточных ререндеров

Компоненты меню и поповеров часто ререндерятся.

Проверяется:

  • мемоизация обработчиков
  • отсутствие лишних состояний
  • стабильность props

Плохой пример:

<DropdownMenu.Item onSel ect={() => doSomething()} />

Лучше:

const handleSelect = useCallback(() => {
  doSomething()
}, [])

Проверка стилизации компонентов

Radix UI не содержит встроенных стилей, поэтому вся визуальная логика реализуется вручную.

Проверка data-атрибутов

Radix предоставляет специальные data-атрибуты:

data-state
data-disabled
data-highlighted

Пример:

[data-state="open"] {
  animation: fadeIn 0.2s;
}

Во время code review проверяется:

  • используются ли data-атрибуты
  • нет ли попыток стилизовать внутренние DOM-структуры

Отсутствие зависимости от DOM структуры

Плохая практика:

.dropdown > div > span {
}

Radix может изменить внутреннюю структуру.

Лучше:

[data-state="open"]

Проверка производительности

Порталы (Portal)

Radix использует React Portal для:

  • Dialog
  • Popover
  • Dropdown

Проверяется:

  • не создаётся ли лишних порталов
  • корректно ли используется контейнер

Пример:

<Dialog.Portal>
  <Dialog.Content />
</Dialog.Portal>

Lazy mounting

Некоторые компоненты поддерживают forceMount.

Проверяется:

  • используется ли forceMount без необходимости

Плохой пример:

<Dialog.Content forceMount />

Это может ухудшить производительность.


Проверка обработки событий

Использование onSelect вместо onClick

Для элементов меню Radix рекомендует onSelect.

Неправильно:

<DropdownMenu.Item onCl ick={handleClick} />

Правильно:

<DropdownMenu.Item onSel ect={handleClick} />

Причина:

  • onSelect корректно интегрируется с клавиатурной навигацией.

Отмена закрытия меню

Иногда требуется отменить закрытие.

Проверяется корректность реализации:

onSel ect={(event) => {
  event.preventDefault()
}}

Проверка тестируемости компонентов

Доступность селекторов

Тесты должны использовать:

  • role
  • aria attributes
  • data attributes

Плохой пример:

document.querySelector(".menu-item")

Правильно:

getByRole("menuitem")

Предсказуемость состояния

Code review должен проверять:

  • можно ли контролировать состояние
  • можно ли открыть компонент программно

Пример:

<Dialog.Root open={open} onOpenCha nge={setOpen}>

Это облегчает тестирование.


Проверка устойчивости интерфейса

Обработка ошибок

Компоненты Radix должны корректно работать даже при отсутствии данных.

Проверяется:

  • защита от undefined
  • отсутствие крашей UI

Плохой пример:

<DropdownMenu.Item>
  {user.name}
</DropdownMenu.Item>

Если user undefined, произойдёт ошибка.


Защита от гонок состояния

Проверяется корректность обновления состояния:

setOpen(prev => !prev)

А не:

setOpen(!open)

Типовой чеклист code review для Radix UI

Архитектура

  • UI отделён от бизнес-логики
  • Radix используется только для интерфейса
  • компоненты имеют понятную структуру

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

  • присутствует Root
  • корректная иерархия
  • не нарушена композиция

Accessibility

  • интерактивные элементы остаются интерактивными
  • не ломается клавиатурная навигация
  • корректная работа focus management

Состояние

  • нет конфликтующих состояний
  • правильно используется controlled/uncontrolled режим

Стили

  • используются data-атрибуты
  • нет зависимости от внутреннего DOM

Производительность

  • отсутствуют лишние порталы
  • нет ненужного forceMount
  • нет лишних ререндеров

События

  • используется onSelect вместо onClick
  • корректно обрабатываются preventDefault

Тестируемость

  • компоненты имеют стабильные роли
  • состояние можно контролировать извне

Автоматизация чеклистов

Часть проверок можно автоматизировать:

ESLint правила

  • запрет div внутри Trigger
  • запрет onClick в Menu.Item
  • проверка forwardRef

Unit тесты

Проверяют:

  • открытие компонентов
  • навигацию клавиатурой
  • фокус

Storybook тестирование

Позволяет проверять:

  • интерактивность
  • accessibility
  • визуальные состояния

Систематическое применение чеклистов code review позволяет поддерживать:

  • высокую доступность интерфейса
  • архитектурную чистоту
  • предсказуемое поведение компонентов
  • масштабируемость UI-системы на базе Radix UI.