Radix UI предоставляет низкоуровневые, полностью доступные и настраиваемые компоненты для React. Один из ключевых аспектов пользовательского интерфейса — управление открытием и закрытием интерактивных элементов, таких как Dropdown, Dialog, Popover, Menu, Accordion. В сложных интерфейсах часто требуется предотвращение автоматического закрытия при определённых условиях, и Radix UI предоставляет для этого гибкие механизмы.
Radix UI поддерживает два подхода к управлению состоянием:
Popover.Root
автоматически закрывается при клике вне области.open и событие
onOpenChange. Этот подход необходим, когда требуется
предотвращать закрытие в некоторых сценариях.Пример использования контролируемого состояния:
import * as Popover from '@radix-ui/react-popover';
import { useState } from 'react';
function Example() {
const [open, setOpen] = useState(false);
return (
<Popover.Root open={open} onOpenCha nge={(nextOpen) => {
if (!shouldPreventClose()) {
setOpen(nextOpen);
}
}}>
<Popover.Trigger>Открыть поповер</Popover.Trigger>
<Popover.Content>
Контент поповера
</Popover.Content>
</Popover.Root>
);
}
function shouldPreventClose() {
// Логика для предотвращения закрытия
return false; // пример: всегда разрешать закрытие
}
Ключевой момент: проп onOpenChange
предоставляет значение true или false, которое
показывает, собирается ли компонент открыться или закрыться. Внутри
обработчика можно реализовать условие для отмены закрытия.
onInteractOutside и onEscapeKeyDownRadix UI позволяет полностью управлять событиями закрытия через события взаимодействия:
onInteractOutside — срабатывает при
клике вне компонента.onEscapeKeyDown — срабатывает при
нажатии клавиши Escape.Эти события можно перехватывать и предотвращать стандартное закрытие:
<Popover.Content
onInteractOuts ide={(event) => {
if (shouldPreventClose()) {
event.preventDefault(); // отменяет закрытие при клике вне
}
}}
onEscapeKeyD own={(event) => {
if (shouldPreventClose()) {
event.preventDefault(); // отменяет закрытие при Escape
}
}}
>
Контент поповера
</Popover.Content>
event.preventDefault() полностью блокирует стандартное
поведение закрытия, что позволяет реализовать сложную логику — например,
подтверждение действий перед закрытием или блокировку закрытия при
незавершённой форме внутри диалога.
Частая задача — запрещать закрытие при взаимодействии с определёнными
внутренними элементами. Radix UI использует концепцию
asChild и возможность проверки таргета
события:
<Popover.Content
onInteractOuts ide={(event) => {
if (event.target.closest('.prevent-close')) {
event.preventDefault();
}
}}
>
<div className="prevent-close">
Клик сюда не закроет поповер
</div>
</Popover.Content>
closest() позволяет проверять, принадлежит ли элемент
определённой группе, и предотвращать закрытие только для этой группы,
оставляя обычное закрытие для остальной области.
В сложных интерфейсах часто есть несколько связанных компонентов (например, меню с подменю, несколько модальных окон), и стандартное автоматическое закрытие может нарушить пользовательский поток. Подход:
open + onOpenChange).onInteractOutside и
onEscapeKeyDown.setOpen(false) только при удовлетворении всех
условий.Пример:
function handleOpenChange(nextOpen) {
if (nextOpen === false && hasUnsavedChanges()) {
alert('Есть несохранённые изменения!');
return; // не закрываем
}
setOpen(nextOpen);
}
Таким образом, Radix UI даёт полный контроль над процессом закрытия компонентов, позволяя строить интерфейсы с логикой подтверждения, блокировки или условного закрытия.
Dialog,
Popover, DropdownMenu) использовать
контролируемый режим, если требуется логика отмены
закрытия.onInteractOutside) или
Escape (onEscapeKeyDown).event.target.closest() или ref для дочерних
элементов.setOpen(false).Эти методы позволяют строить сложные, безопасные и предсказуемые взаимодействия с пользователем, исключая случайное закрытие важных компонентов.