跳到内容
+

自定义插槽和子组件

了解如何覆盖 MUI X 组件的各个部分。

什么是插槽?

插槽是组件的一部分,可以被覆盖和/或自定义

其中一些插槽允许您为 MUI X 组件提供您自己的 UI 原语。这是 Data Grid 组件上所有 baseXXX 组件(baseButtonbaseSelect 等)的作用。这些插槽接收的 props 应该尽可能通用,以便于与任何其他设计系统对接。

其他插槽允许您使用专门为此组件构建的自定义 UI 覆盖 MUI X UI 组件的各个部分。这是诸如 DateCalendar 组件上的 calendarHeader 或 Rich Tree View 组件上的 item 等插槽的作用。这些插槽接收特定于 UI 这部分的 props,并且很可能不会在您的应用程序中重复使用。

基本用法

如何覆盖插槽?

您可以通过为 slots prop 提供自定义组件来覆盖插槽

Enter 开始编辑

如何自定义插槽?

您可以使用 slotProps prop 将 props 传递给任何插槽

Enter 开始编辑

您也可以在同一组件上同时使用 slotsslotProps

Enter 开始编辑

大多数插槽还支持 slotProps 的回调版本。此回调接收一个对象,其中包含有关组件当前状态的信息,该信息可能因所使用的插槽而异

Enter 开始编辑

正确用法

插槽是一个 React 组件;因此,它应该在两次渲染之间保持相同的 JavaScript 引用。如果组件的 JavaScript 引用在两次渲染之间发生变化,React 将重新挂载它。您可以通过不在 slots prop 中内联组件定义来避免这种情况。

以下前两个示例是有 bug 的,因为日历 header 会在每次击键后重新挂载,从而导致焦点丢失。

// ❌ The `calendarHeader` slot is re-defined each time the parent component renders,
// causing the component to remount.
function MyApp() {
  const [name, setName] = React.useState('');
  return (
    <DateCalendar
      slots={{
        calendarHeader: () => (
          <input value={name} onChange={(event) => setName(event.target.value)} />
        ),
      }}
    />
  );
}
// ❌ The `calendarHeader` slot is re-defined each time `name` is updated,
// causing the component to remount.
function MyApp() {
  const [name, setName] = React.useState('');

  const CustomCalendarHeader = React.useCallback(
    () => <input value={name} onChange={(event) => setName(event.target.value)} />,
    [name],
  );

  return <DateCalendar slots={{ calendarHeader: CustomCalendarHeader }} />;
}
// ✅ The `calendarHeader` slot is defined only once, it will never remount.
const CustomCalendarHeader = ({ name, setName }) => (
  <input value={name} onChange={(event) => setName(event.target.value)} />
);

function MyApp() {
  const [name, setName] = React.useState('');
  return (
    <DateCalendar
      slots={{ calendarHeader: CustomCalendarHeader }}
      slotProps={{ calendarHeader: { name, setName } }}
    />
  );
}

TypeScript 的用法

类型化自定义插槽

如果您想确保自定义插槽组件的类型安全,可以使用 PropsFromSlot 接口声明您的组件

function CustomCalendarHeader({
  currentMonth,
}: PropsFromSlot<DateCalendarSlots<Dayjs>['calendarHeader']>) {
  return <div>{currentMonth?.format('MM-DD-YYYY')}</div>;
}

使用附加 props

如果您正在将附加 props 传递给您的插槽,您可以将它们添加到您的自定义组件接收的 props 中

interface CustomCalendarHeaderProps
  extends PropsFromSlot<DateCalendarSlots<Dayjs>['calendarHeader']> {
  displayWeekNumber: boolean;
  setDisplayWeekNumber: (displayWeekNumber: boolean) => void;
}

然后,您可以在自定义组件中使用这些 props,并访问由宿主组件提供的 props 和您添加的 props

function CustomCalendarHeader({
  displayWeekNumber,
  setDisplayWeekNumber,
  ...other
}: CustomCalendarHeaderProps) {
  return (
    <Stack>
      <DisplayWeekNumberToggle
        value={displayWeekNumber}
        onChange={setDisplayWeekNumber}
      />
      <PickersCalendarHeader {...other} />
    </Stack>
  );
}

如果您的自定义组件具有与默认组件不同的类型,则需要将其强制转换为正确的类型。如果您使用 slotProps 将附加 props 传递给自定义组件,则可能会发生这种情况。如果我们以 calendarHeader 插槽为例,您可以按如下所示强制转换您的自定义组件

function MyApp() {
  const [displayWeekNumber, setDisplayWeekNumber] = React.useState(false);
  return (
    <DateCalendar
      displayWeekNumber={displayWeekNumber}
      slots={{
        calendarHeader:
          CustomCalendarHeader as DateCalendarSlots<Dayjs>['calendarHeader'],
      }}
      slotProps={{
        calendarHeader: {
          displayWeekNumber,
          setDisplayWeekNumber,
        } as DateCalendarSlotProps<Dayjs>['calendarHeader'],
      }}
    />
  );
}
SMTWTFS

使用模块增强

如果您正在使用 Data Grid 包之一,您还可以使用模块增强来让 TypeScript 知道您的自定义 props

declare module '@mui/x-data-grid' {
  interface ToolbarPropsOverrides {
    name: string;
    setName: (name: string) => void;
  }
}

然后,您可以无需任何类型转换即可使用您的自定义插槽

function CustomToolbar({ name, setName }: PropsFromSlot<GridSlots['toolbar']>) {
  return <input value={name} onChange={(event) => setName(event.target.value)} />;
}

function MyApp() {
  const [name, setName] = React.useState('');
  return (
    <DataGrid
      rows={[]}
      columns={[]}
      slots={{ toolbar: CustomToolbar }}
      slotProps={{
        toolbar: { name, setName },
      }}
    />
  );
}

有关更多详细信息,请参阅Data Grid - 自定义插槽和子组件——带有 TypeScript 的自定义插槽 props

X 组件的插槽

您可以在每个组件的 API 文档中找到插槽列表(例如 DataGridDatePickerBarChartRichTreeView)。

大多数插槽都有一个独立的文档示例来展示如何使用它们