Вложенные списки

В библиотеке MUI для работы со списками используется компонент List, который позволяет создавать упорядоченные и неупорядоченные элементы интерфейса с удобной структурой и стилизацией. Вложенные списки реализуются с помощью сочетания компонентов ListItem, ListItemText, Collapse и List внутри других ListItem.

Основные компоненты

  1. List Корневой компонент, содержащий элементы списка. Поддерживает вертикальную и горизонтальную ориентацию, а также возможность применения стилей через пропсы sx или className.

  2. ListItem Элемент списка. Может быть интерактивным (с button={true}) или статическим. Позволяет добавлять иконки, чекбоксы и вторичные тексты.

  3. ListItemText Компонент для отображения текста в элементе списка. Поддерживает primary и secondary тексты, что удобно для вложенных структур.

  4. Collapse Используется для скрытия и отображения вложенных списков с анимацией. Часто применяется вместе с состоянием компонента (useState) для контроля раскрытия.

Пример вложенного списка

import React, { useState } from 'react';
import { List, ListItem, ListItemText, Collapse, ListItemIcon } from '@mui/material';
import InboxIcon from '@mui/icons-material/Inbox';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';

function NestedListExample() {
  const [open, setOpen] = useState(false);

  const handleClick = () => {
    setOpen(!open);
  };

  return (
    <List sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}>
      <ListItem button onCl ick={handleClick}>
        <ListItemIcon>
          <InboxIcon />
        </ListItemIcon>
        <ListItemText primary="Папка 1" />
        {open ? <ExpandLess /> : <ExpandMore />}
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <List component="div" disablePadding>
          <ListItem sx={{ pl: 4 }}>
            <ListItemText primary="Файл 1-1" />
          </ListItem>
          <ListItem sx={{ pl: 4 }}>
            <ListItemText primary="Файл 1-2" />
          </ListItem>
        </List>
      </Collapse>
    </List>
  );
}

export default NestedListExample;

Важные моменты при работе с вложенными списками

  • Управление состоянием раскрытия Для каждого вложенного списка нужно использовать отдельное состояние. При динамическом рендеринге списков удобно хранить объект или массив флагов open для разных элементов.

  • Отступы и стилизация Для создания визуальной иерархии используют sx={{ pl: 4 }}, где pl — padding-left. Значение увеличивается с каждым уровнем вложенности.

  • Иконки для раскрытия/сворачивания Компоненты ExpandLess и ExpandMore помогают пользователю понимать текущий статус вложенного списка. Их можно динамически менять на основе состояния.

  • Производительность Для больших списков рекомендуется использовать unmountOnExit в компоненте Collapse, чтобы скрытые элементы не рендерились в DOM до раскрытия.

Динамические вложенные списки

В MUI легко строить списки на основе данных из массива объектов:

const data = [
  {
    id: 1,
    name: 'Папка A',
    children: [
      { id: 11, name: 'Файл A1' },
      { id: 12, name: 'Файл A2' },
    ],
  },
  {
    id: 2,
    name: 'Папка B',
    children: [
      { id: 21, name: 'Файл B1' },
      { id: 22, name: 'Файл B2' },
    ],
  },
];

Рекурсивный рендер элементов:

function RecursiveList({ items }) {
  const [openIds, setOpenIds] = useState([]);

  const handleClick = (id) => {
    setOpenIds((prev) =>
      prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
    );
  };

  return (
    <List>
      {items.map((item) => (
        <div key={item.id}>
          <ListItem button onCl ick={() => handleClick(item.id)}>
            <ListItemText primary={item.name} />
            {item.children ? (openIds.includes(item.id) ? <ExpandLess /> : <ExpandMore />) : null}
          </ListItem>
          {item.children && (
            <Collapse in={openIds.includes(item.id)} timeout="auto" unmountOnExit>
              <RecursiveList items={item.children} />
            </Collapse>
          )}
        </div>
      ))}
    </List>
  );
}

Советы по удобству и доступности

  • ARIA-атрибуты Для вложенных списков полезно использовать aria-expanded, aria-controls на кнопках, управляющих раскрытием, чтобы улучшить доступность.

  • Клавиатурная навигация Элементы с button={true} автоматически получают возможность навигации с клавиатуры, что важно для пользователей с ограничениями.

  • Согласованность дизайна Следует использовать одинаковые отступы, размеры шрифтов и иконки на всех уровнях вложенности, чтобы интерфейс оставался предсказуемым и читабельным.

  • Анимация раскрытия Collapse добавляет плавность при открытии и закрытии списков, предотвращая резкое изменение интерфейса и улучшая пользовательский опыт.

Вложенные списки MUI обеспечивают гибкую структуру для организации элементов интерфейса, позволяют легко управлять состояниями раскрытия и поддерживают высокую производительность даже при больших объемах данных.