Render props

В библиотеке MUI (Material-UI) Render Props представляют собой технику, позволяющую передавать компоненту функцию, которая управляет рендерингом внутренней части компонента. Эта концепция особенно полезна для компонентов, у которых есть сложная логика состояния или поведения, но требуется гибкость в том, как отображается пользовательский интерфейс.

Пример базового использования в MUI:

import React from 'react';
import { Popper, Button } from '@mui/material';

function Example() {
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handleClick = (event) => {
    setAnchorEl(anchorEl ? null : event.currentTarget);
  };

  const open = Boolean(anchorEl);

  return (
    <div>
      <Button onCl ick={handleClick}>Toggle Popper</Button>
      <Popper open={open} anchorEl={anchorEl}>
        {({ placement }) => (
          <div style={{ padding: 10, background: 'lightgray' }}>
            Popper размещен {placement}
          </div>
        )}
      </Popper>
    </div>
  );
}

Здесь Popper использует render prop для передачи объекта с информацией о размещении (placement) внутрь функции, которая возвращает JSX. Это позволяет динамически изменять разметку в зависимости от состояния компонента.


Отличие Render Props от обычных children

  • Обычные children — это статические элементы JSX, которые отображаются внутри компонента.
  • Render Props — это функция, которая принимает параметры состояния компонента и возвращает JSX.

Пример сравнения:

// Children
<Popper open={open} anchorEl={anchorEl}>
  <div>Статический контент</div>
</Popper>

// Render Prop
<Popper open={open} anchorEl={anchorEl}>
  {({ placement }) => <div>Текущее размещение: {placement}</div>}
</Popper>

Render Props обеспечивают гибкость, позволяя компонентах передавать данные о состоянии, которые нельзя получить напрямую через обычные дочерние элементы.


Частые компоненты MUI с Render Props

В MUI ряд компонентов активно используют Render Props:

  1. Popper – предоставляет данные о положении, открытии и закрытии.
  2. Autocomplete – позволяет кастомизировать отображение списка опций через renderOption и renderInput.
  3. Menu – через MenuListProps можно управлять рендерингом внутренних элементов.
  4. Tooltip – с помощью children как функции можно полностью контролировать, какой элемент оборачивается тултипом.

Пример кастомизации Autocomplete:

import { Autocomplete, TextField } from '@mui/material';

const options = ['Apple', 'Banana', 'Cherry'];

function FruitSelector() {
  return (
    <Autocomplete
      options={options}
      renderInput={(params) => <TextField {...params} label="Выберите фрукт" />}
      renderOption={(props, option) => (
        <li {...props} style={{ color: option === 'Banana' ? 'yellow' : 'black' }}>
          {option}
        </li>
      )}
    />
  );
}

Здесь renderInput и renderOption — это render props, позволяющие управлять разметкой поля ввода и отдельных опций списка.


Передача параметров в Render Props

Render Props могут получать разные параметры, в зависимости от компонента MUI. Например, в Popper это:

  • placement – положение относительно якоря (top, bottom, left, right).
  • arrowProps – стили для стрелки, если она используется.
  • TransitionProps – управление анимацией появления.

В Autocomplete:

  • props – набор атрибутов для элемента списка, необходимых для корректного рендеринга.
  • option – конкретная опция списка.
  • state – текущее состояние автокомплита (inputValue, value, focused и др.).

Пример использования параметров:

<Popper open={open} anchorEl={anchorEl}>
  {({ placement, arrowProps }) => (
    <div>
      <div {...arrowProps} style={{ width: 10, height: 10, background: 'gray' }} />
      <span>Размещено {placement}</span>
    </div>
  )}
</Popper>

Интеграция с кастомными компонентами

Render Props позволяют интегрировать MUI с собственными компонентами, сохраняя управление состоянием и стилизацией.

Пример с кастомной кнопкой внутри Menu:

import { Menu, MenuItem, Button } from '@mui/material';
import React from 'react';

function CustomMenu() {
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handleClick = (event) => setAnchorEl(event.currentTarget);
  const handleClose = () => setAnchorEl(null);

  return (
    <div>
      <Button onCl ick={handleClick}>Открыть меню</Button>
      <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onCl ose={handleClose}>
        {({ onItemClick }) => (
          <>
            <MenuItem onCl ick={() => { onItemClick(); console.log('Первый элемент'); }}>Первый</MenuItem>
            <MenuItem onCl ick={() => { onItemClick(); console.log('Второй элемент'); }}>Второй</MenuItem>
          </>
        )}
      </Menu>
    </div>
  );
}

Даже если MUI не документирует onItemClick для всех компонентов, практика render props позволяет добавить кастомные обработчики, сохраняя согласованность с внутренним API.


Плюсы Render Props

  • Позволяют полностью контролировать визуализацию компонентов.
  • Поддерживают передачу состояния и данных компонента в кастомную разметку.
  • Совместимы с анимациями, условным рендерингом и стилями.
  • Обеспечивают повторное использование логики без копирования JSX.

Рекомендации по использованию

  1. Использовать render props, когда требуется динамическая кастомизация контента.
  2. Для простого статического рендеринга оставлять обычные дочерние элементы.
  3. Передавать только необходимые параметры, избегая глубокой зависимости от внутреннего API компонента.
  4. Комбинировать с хуками состояния MUI (useAutocomplete, usePopper) для более сложных сценариев.

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