自訂插槽和子組件
了解如何覆寫 MUI X 組件的部分。
什麼是插槽?
其中一些插槽允許您為 MUI X 組件提供自己的 UI 基礎元件。這是 Data Grid 組件上所有 baseXXX
組件 (baseButton
, baseSelect
, ...) 的作用。這些插槽接收的 props 應該盡可能通用,以便於與任何其他設計系統介接。
其他插槽允許您使用專為此組件構建的自訂 UI 來覆寫 MUI X UI 組件的部分。這是 DateCalendar
組件上的 calendarHeader
插槽或 Rich Tree View 組件上的 item
插槽的作用。這些插槽接收特定於 UI 這部分的 props,並且很可能不會在您的應用程式中重複使用。
基本用法
如何覆寫插槽?
您可以通過將自訂組件提供給 slots
prop 來覆寫插槽
如何自訂插槽?
您可以使用 slotProps
prop 將 props 傳遞給任何插槽
您也可以在同一個組件上同時使用 slots
和 slotProps
大多數插槽也支援 slotProps
的回呼版本。此回呼接收一個物件,其中包含有關組件目前狀態的資訊,該資訊可能因所使用的插槽而異
正確用法
插槽是一個 React 組件;因此,它應該在兩次渲染之間保持相同的 JavaScript 參考。如果組件的 JavaScript 參考在兩次渲染之間發生變化,React 將重新掛載它。您可以通過不在 slots
prop 中內聯組件定義來避免這種情況。
以下的前兩個範例有錯誤,因為日曆標頭會在每次按鍵後重新掛載,導致焦點丟失。
// ❌ 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'],
}}
/>
);
}
使用模組擴充
如果您正在使用 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 文件中找到插槽列表(例如DataGrid、DatePicker、BarChart 或 RichTreeView)。
大多數插槽都有獨立的文件範例來展示如何使用它們