Ленивая загрузка (lazy loading) — это подход, при котором часть контента или компонентов интерфейса загружается только в момент их фактического использования. В контексте Radix UI это особенно актуально для сложных модальных окон, аккордеонов, тултипов и других интерактивных компонентов, где рендерить весь DOM сразу нецелесообразно.
Radix UI предоставляет компонент Presence и встроенные
возможности управления состоянием отображения элементов. Ключевая идея —
рендерить дочерние элементы только тогда, когда компонент активен или
становится видимым на экране.
Пример концепции ленивой загрузки для модального окна:
import * as Dialog from '@radix-ui/react-dialog';
import { useState } from 'react';
function LazyDialog() {
const [isOpen, setIsOpen] = useState(false);
return (
<Dialog.Root open={isOpen} onOpenCha nge={setIsOpen}>
<Dialog.Trigger>Открыть модальное окно</Dialog.Trigger>
{isOpen && (
<Dialog.Content>
<Dialog.Title>Заголовок окна</Dialog.Title>
<Dialog.Description>Описание содержимого</Dialog.Description>
</Dialog.Content>
)}
</Dialog.Root>
);
}
Здесь компонент Dialog.Content рендерится только при
открытии диалога. Такой подход уменьшает нагрузку на DOM и ускоряет
начальную загрузку страницы.
asChild для интеграции с ленивым рендерингомС помощью свойства asChild можно обернуть любой
компонент сторонней библиотеки, сохранив управление поведением Radix UI.
Это позволяет динамически подключать контент только при необходимости,
не нарушая взаимодействие с API Radix.
import * as Tooltip from '@radix-ui/react-tooltip';
import { Suspense, lazy } from 'react';
const LazyTooltipContent = lazy(() => import('./TooltipContent'));
function LazyTooltip() {
return (
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger>Наведи на меня</Tooltip.Trigger>
<Suspense fallback={<div>Загрузка...</div>}>
<Tooltip.Content asChild>
<LazyTooltipContent />
</Tooltip.Content>
</Suspense>
</Tooltip.Root>
</Tooltip.Provider>
);
}
Использование React.lazy и Suspense
позволяет держать основной бандл компактным и загружать тяжелые элементы
только при необходимости.
Для компонентов Accordion и больших списков
рекомендуется создавать отдельные ленивые панели, которые рендерятся
только при раскрытии.
import * as Accordion from '@radix-ui/react-accordion';
import { lazy, Suspense } from 'react';
const LazyPanelContent = lazy(() => import('./PanelContent'));
function LazyAccordion() {
return (
<Accordion.Root type="single" collapsible>
<Accordion.Item value="item-1">
<Accordion.Header>
<Accordion.Trigger>Панель 1</Accordion.Trigger>
</Accordion.Header>
<Suspense fallback={<div>Загрузка...</div>}>
<Accordion.Content>
<LazyPanelContent />
</Accordion.Content>
</Suspense>
</Accordion.Item>
</Accordion.Root>
);
}
Такой подход экономит ресурсы, особенно при работе с большими списками данных или тяжелым UI-контентом.
Для компонентов, которые становятся видимыми при прокрутке, можно использовать Intersection Observer совместно с Radix UI. Это позволяет рендерить элементы только когда пользователь их видит, улучшая производительность страницы.
import { useEffect, useRef, useState } from 'react';
import * as Tooltip from '@radix-ui/react-tooltip';
function LazyInViewTooltip() {
const ref = useRef(null);
const [inView, setInView] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting));
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return (
<div ref={ref}>
{inView && (
<Tooltip.Root>
<Tooltip.Trigger>Интерактивный элемент</Tooltip.Trigger>
<Tooltip.Content>Контент загружается при видимости</Tooltip.Content>
</Tooltip.Root>
)}
</div>
);
}
open и onOpenChange идеально подходят для
контроля ленивого рендеринга.React.memo для предотвращения ненужного
повторного рендеринга.Ленивая загрузка в Radix UI — это не просто отложенный рендеринг. Она тесно интегрируется с системой управления состоянием компонентов, позволяя создавать отзывчивые и производительные интерфейсы даже при большом объеме интерактивного контента.