Перейти к содержимому
v2.5.0
English
Русский
Документация

Slot

Slot — низкоуровневый примитив полиморфной композиции, на котором построены API asChild в VegaUI.

Паттерны

Полиморфный триггер

Когда использовать:

  • низкоуровневый примитив-триггер должен рендериться как элемент, выбранный пользователем библиотеки

Заметки по композиции:

  • гарантируйте ровно один дочерний элемент
  • сохраняйте подходящий для задачи семантический элемент

Компромиссы:

  • гибко
  • проще ошибиться, чем при фиксированном рендере
polymorphic-trigger.tsx · 44 строки
import type { CSSProperties } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Text,
} from '@vega-ui/react';

Когда использовать:

  • навигация должна сохранить семантику ссылки, но позаимствовать стили кнопки

Заметки по композиции:

  • выбирайте элемент <a> осознанно
  • сохраняйте семантику навигации явной

Компромиссы:

  • сохраняет поведение ссылки
  • легко злоупотребить там, где обычная ссылка была бы понятнее
styled-link-action.tsx · 16 строк
import { Button, Icon } from '@vega-ui/react';
import { BookOpenIcon } from '@vega-ui/icons';

export default function StyledLinkAction() {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 'var(--spacing-4)' }}>
      <Button type='button'>Create API key</Button>
      <Button asChild variant='secondary' appearance='ghost'>
        <a href='/docs/api/authentication'>
          <Icon size='sm'><BookOpenIcon /></Icon>
          Read the API guide
        </a>

Написание низкоуровневых обёрток

Когда использовать:

  • внутренностям дизайн-системы нужно добавить поведение в один дочерний элемент

Заметки по композиции:

  • тщательно тестируйте слияние обработчиков событий
  • сохраняйте семантику выбранного дочернего элемента явной

Компромиссы:

  • фундамент для полиморфизма
  • легко внести тонкие семантические регрессии
low-level-wrapper-authoring.tsx · 34 строки
import { type ReactNode, useState } from 'react';
import { Button, Slot } from '@vega-ui/react';

interface TrackClickProps {
  event: string
  onTrack(event: string): void
  children: ReactNode
}

function TrackClick({ event, onTrack, children }: TrackClickProps) {
  return <Slot onClick={() => onTrack(`analytics: ${event}`)}>{children}</Slot>;
}

Сравнение

Быстрое правило выбора

Используйте Slot, только когда строите низкоуровневую полиморфную композицию. В обычном продуктовом коде предпочитайте высокоуровневые API asChild.

Slot или asChild у компонентов

  • Используйте Slot, когда пишете примитивы или обёртки.
  • Используйте asChild у существующих компонентов VegaUI в продуктовом коде.

Slot или прямой рендер элемента

  • Рендерите элемент напрямую, когда слияние пропсов не нужно.
  • Используйте Slot, когда пропсы родителя нужно слить с выбранным дочерним элементом.

Выбирайте этот компонент, когда

  • вы расширяете внутренности дизайн-системы
  • вам нужна семантика слияния пропсов с единственным дочерним элементом

Не выбирайте этот компонент, когда

  • компонент уже предоставляет asChild
  • вам нужен просто обычный элемент

Решение проблем

Композиция падает во время выполнения

Симптом

Рендер завершается ошибкой Slot о форме дочернего элемента.

Вероятная причина

Дочерний элемент не является ровно одним валидным React-элементом.

Как проверить

  • посмотрите, что на самом деле передаётся в children

Решение

Передавайте один валидный React-элемент, а не обычный текст, массивы или фрагменты с несколькими узлами.

Интерфейс выглядит правильно, но семантика неверна

Симптом

Элемент в слоте выглядит правильно стилизованным, но поведение или доступность нарушены.

Вероятная причина

У выбранного дочернего элемента неподходящая для задачи семантика.

Как проверить

  • посмотрите итоговый отрендеренный тег
  • сравните его с задуманным семантическим элементом

Решение

Выберите правильный дочерний элемент и помните, что Slot меняет только рендер, но не семантику.

Поведение событий кажется неожиданным

Симптом

Обработчики родителя и дочернего элемента словно конфликтуют.

Вероятная причина

Слияние пропсов создало пересекающееся поведение.

Как проверить

  • изучите слитые пропсы на итоговом элементе
  • протестируйте обработчики родителя и дочернего элемента по отдельности

Решение

Упростите композицию и явно определите, кто отвечает за логику взаимодействия.