Breaking changes — это изменения в библиотеке или API, которые нарушают обратную совместимость. После внедрения таких изменений существующий код, написанный для предыдущих версий, перестаёт работать без модификации. В экосистеме React и современных библиотек пользовательского интерфейса breaking changes чаще всего связаны с:
В контексте библиотеки Radix UI breaking changes могут влиять на архитектуру компонентов, систему композиции, правила управления состоянием, а также на внутренние примитивы, на которых строится интерфейс.
Понимание природы breaking changes особенно важно при обновлении библиотек между мажорными версиями, поскольку именно они содержат изменения, нарушающие обратную совместимость.
Radix UI использует принцип Semantic Versioning (SemVer). Версия пакета имеет формат:
MAJOR.MINOR.PATCH
Увеличение MAJOR версии означает наличие breaking changes.
Пример:
@radix-ui/react-dialog 1.x → 2.x
В этом случае возможны:
Изменения, добавляющие функциональность, но не нарушающие совместимость.
Пример:
Исправления ошибок без изменения API.
Одной из самых распространённых форм breaking changes является изменение props.
<Dialog open={isOpen} onOpenCha nge={setOpen}>
<Dialog.Content>
...
</Dialog.Content>
</Dialog>
После обновления API может измениться структура компонента:
<Dialog.Root open={isOpen} onOpenCha nge={setOpen}>
<Dialog.Content>
...
</Dialog.Content>
</Dialog.Root>
В таком случае требуется изменить структуру компонентов.
В процессе развития библиотеки некоторые свойства помечаются как deprecated, а затем удаляются.
Пример устаревшего свойства:
<Popover align="center">
После breaking change свойство может быть заменено:
<Popover align="middle">
Использование старого значения приводит к ошибке или некорректному поведению.
Radix UI активно использует композицию компонентов. Иногда структура может меняться.
<Tabs>
<Tabs.List>
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">
Content
</Tabs.Content>
</Tabs>
<Tabs.Root>
<Tabs.List>
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">
Content
</Tabs.Content>
</Tabs.Root>
Изменение требует обновления всех мест использования компонента.
Breaking changes могут затрагивать импорты.
import { Dialog } from "@radix-ui/react-dialog";
import * as Dialog from "@radix-ui/react-dialog";
Такой подход используется для поддержки namespace imports, который облегчает масштабирование API.
Иногда библиотека меняет дефолтное поведение компонентов.
Пример:
Popover автоматически закрывался при клике вне компонента.
Поведение может измениться и потребовать явного указания:
<Popover.Root modal={false}>
Без явного указания логика работы может отличаться от прежней.
Radix UI активно использует порталы (React Portals) для модальных окон, тултипов и других overlay-компонентов.
Breaking change может изменить:
Пример:
<Dialog.Portal>
<Dialog.Content />
</Dialog.Portal>
<Dialog.Portal container={document.body}>
<Dialog.Content />
</Dialog.Portal>
Теперь контейнер портала может требовать явного указания.
Radix UI использует библиотеку Floating UI для позиционирования всплывающих элементов.
Breaking changes могут включать:
Пример:
<DropdownMenu.Content sideOffset={5}>
<DropdownMenu.Content alignOffset={5}>
Radix UI уделяет большое внимание accessibility (a11y). Иногда breaking changes внедряются для соответствия стандартам:
Изменения могут включать:
Например:
<Dialog.Title>
может стать обязательным элементом внутри
Dialog.Content.
Radix UI активно использует TypeScript, поэтому breaking changes часто затрагивают типизацию.
Пример:
type Align = "start" | "center" | "end"
type Align = "start" | "center" | "end" | "stretch"
Иногда типы наоборот ужесточаются, что приводит к ошибкам компиляции.
При переходе на новую major-версию обычно выполняется последовательность действий.
Каждый пакет Radix UI публикует changelog, где перечислены breaking changes.
Пример записи:
Breaking:
- renamed Dialog to Dialog.Root
- removed deprecated prop `forceMount`
В кодовой базе ищутся:
Иногда это можно автоматизировать с помощью codemods.
Если изменилась структура импорта:
import { Dialog } from "@radix-ui/react-dialog"
заменяется на:
import * as Dialog from "@radix-ui/react-dialog"
После изменения API требуется обновить иерархию компонентов.
Например:
Dialog → Dialog.Root
Даже если код компилируется, необходимо проверить:
Breaking changes иногда затрагивают именно поведение.
В крупных проектах обновление библиотеки может требовать изменения сотен компонентов. Для ускорения процесса используются:
Codemods — это скрипты автоматической трансформации кода.
Обычно реализуются с использованием:
Пример преобразования:
Dialog → Dialog.Root
можно выполнить автоматически.
Некоторые команды добавляют eslint rules, которые обнаруживают устаревший API.
Пример предупреждения:
Deprecated Radix API detected: Dialog should be replaced with Dialog.Root
TypeScript помогает выявлять breaking changes на этапе компиляции.
Если типы изменились:
Property 'alignOffset' does not exist
это указывает на необходимость обновления API.
Лучше обновлять версии последовательно:
1.0 → 1.5 → 2.0
а не напрямую:
1.0 → 2.0
Создаётся ветка:
radix-upgrade
В ней:
Breaking changes легче обнаружить при наличии:
Особенно полезны визуальные тесты, так как Radix UI отвечает за интерфейс.
На практике чаще всего встречаются следующие изменения:
*.RootRadix UI построен вокруг нескольких архитектурных принципов.
Компоненты не содержат стилей и предоставляют только поведение.
Поэтому breaking changes чаще затрагивают:
Radix UI использует множество вложенных компонентов:
Dialog.Root
Dialog.Trigger
Dialog.Portal
Dialog.Content
Dialog.Title
Dialog.Description
Изменение любого элемента этой структуры может стать breaking change.
Многие компоненты поддерживают два режима:
controlled
uncontrolled
Например:
open
defaultOpen
onOpenChange
Изменения в этой логике могут потребовать переписывания части кода.
Эффективная стратегия работы с breaking changes включает:
Такой подход минимизирует риск критических ошибок при обновлении Radix UI и позволяет безопасно адаптировать кодовую базу к изменениям библиотеки.