В проектах на JavaScript и TypeScript, использующих Radix UI, код-ревью выполняет не только традиционные задачи проверки качества кода, но и контролирует правильность использования примитивов библиотеки: доступность компонентов, корректную композицию, управление состоянием и соблюдение архитектурных принципов.
Radix UI предоставляет низкоуровневые, доступные UI-примитивы, поэтому большая часть логики поведения и визуального слоя реализуется разработчиком. Именно по этой причине наличие формализованных чеклистов code review критически важно: они позволяют системно проверять корректность интеграции компонентов и предотвращать накопление технического долга.
Чеклист code review для Radix UI обычно включает следующие категории:
Компоненты 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>
)
}
Проблема:
Правильная архитектура:
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 проверяется:
Ошибки композиции часто приводят к:
Каждый Radix-компонент имеет Root, управляющий состоянием.
Пример:
Dialog.Root
Popover.Root
DropdownMenu.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}>
Такое использование создаёт конфликт состояния.
Одно из главных преимуществ Radix UI — встроенная поддержка accessibility. Однако неправильное использование компонентов может её нарушить.
Radix автоматически устанавливает:
Code review должен проверять, что эти механизмы не были сломаны кастомизацией.
Например:
Плохой код:
<Dialog.Trigger asChild>
<div>Open</div>
</Dialog.Trigger>
Проблема:
div не является интерактивным элементомПравильно:
<Dialog.Trigger asChild>
<button>Open</button>
</Dialog.Trigger>
Radix обеспечивает поддержку:
Во время code review проверяется:
Плохой пример:
onKeyD own={(e) => e.preventDefault()}
Это может нарушить встроенную навигацию.
Radix автоматически управляет фокусом:
Проверяется:
Плохой пример:
useEffect(() => {
document.querySelector("input")?.focus()
})
Это может конфликтовать с Radix.
asChildRadix поддерживает замену DOM-элементов через
asChild.
Пример:
<Dialog.Trigger asChild>
<Button />
</Dialog.Trigger>
Code review должен проверять:
refПроблемный компонент:
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 уже управляется внутри.
Компоненты меню и поповеров часто ререндерятся.
Проверяется:
Плохой пример:
<DropdownMenu.Item onSel ect={() => doSomething()} />
Лучше:
const handleSelect = useCallback(() => {
doSomething()
}, [])
Radix UI не содержит встроенных стилей, поэтому вся визуальная логика реализуется вручную.
Radix предоставляет специальные data-атрибуты:
data-state
data-disabled
data-highlighted
Пример:
[data-state="open"] {
animation: fadeIn 0.2s;
}
Во время code review проверяется:
Плохая практика:
.dropdown > div > span {
}
Radix может изменить внутреннюю структуру.
Лучше:
[data-state="open"]
Radix использует React Portal для:
Проверяется:
Пример:
<Dialog.Portal>
<Dialog.Content />
</Dialog.Portal>
Некоторые компоненты поддерживают forceMount.
Проверяется:
forceMount без необходимостиПлохой пример:
<Dialog.Content forceMount />
Это может ухудшить производительность.
Для элементов меню Radix рекомендует onSelect.
Неправильно:
<DropdownMenu.Item onCl ick={handleClick} />
Правильно:
<DropdownMenu.Item onSel ect={handleClick} />
Причина:
onSelect корректно интегрируется с клавиатурной
навигацией.Иногда требуется отменить закрытие.
Проверяется корректность реализации:
onSel ect={(event) => {
event.preventDefault()
}}
Тесты должны использовать:
Плохой пример:
document.querySelector(".menu-item")
Правильно:
getByRole("menuitem")
Code review должен проверять:
Пример:
<Dialog.Root open={open} onOpenCha nge={setOpen}>
Это облегчает тестирование.
Компоненты Radix должны корректно работать даже при отсутствии данных.
Проверяется:
undefinedПлохой пример:
<DropdownMenu.Item>
{user.name}
</DropdownMenu.Item>
Если user undefined, произойдёт ошибка.
Проверяется корректность обновления состояния:
setOpen(prev => !prev)
А не:
setOpen(!open)
Часть проверок можно автоматизировать:
div внутри TriggeronClick в Menu.ItemПроверяют:
Позволяет проверять:
Систематическое применение чеклистов code review позволяет поддерживать: