块编辑器开发文档

💡 云策文档标注

概述

SlotFill 是 WordPress 组件库中的一对 React 组件,用于实现“门户”渲染模式,允许开发者将 React UI 渲染到 React 元素树的其他位置,以增强组件的可扩展性。

关键要点

  • Slot 和 Fill 组件需配合 SlotFillProvider 使用,Slot 定义渲染位置,Fill 提供内容。
  • 支持通过 name 属性匹配 Slot 和 Fill,可使用字符串或 Symbol 作为名称。
  • 提供 createSlotFill 辅助方法简化 Slot 和 Fill 的创建过程。
  • Slot 的 bubblesVirtually 属性影响事件冒泡和 React 上下文传播行为,可选值为 true 或 false。
  • Slot 可通过 fillProps 向 Fill 传递额外信息,Fill 可使用函数作为子元素接收这些 props。

代码示例

import {
    SlotFillProvider,
    Slot,
    Fill,
    Panel,
    PanelBody,
} from '@wordpress/components';

const MySlotFillProvider = () => {
    const MyPanelSlot = () => (
        <Panel header="Panel with slot">
            <PanelBody>
                <Slot name="MyPanelSlot" />
            </PanelBody>
        </Panel>
    );

    MyPanelSlot.Content = () => <Fill name="MyPanelSlot">Panel body</Fill>;

    return (
        <SlotFillProvider>
            <MyPanelSlot />
            <MyPanelSlot.Content />
        </SlotFillProvider>
    );
};

注意事项

  • SlotFillProvider 不接受任何 props(除了 children),仅用于协调 Slot 和 Fill 的渲染。
  • 当 bubblesVirtually=true 时,Slot 会渲染一个包装 DOM 元素(默认为 div),并接受 className、style 等 props 进行自定义。
  • 当 bubblesVirtually=false 时,Slot 可接受 children 函数参数,用于处理 fills 数组,例如渲染占位符或条件包装器。

📄 原文内容

Slot and Fill are a pair of components which enable developers to render React UI elsewhere in a React element tree, a pattern often referred to as “portal” rendering. It is a pattern for component extensibility, where a single Slot may be occupied by multiple Fills rendered in different parts of the application.

Slot/Fill was originally inspired by the react-slot-fill library.

Usage

At the root of your application, you must render a SlotFillProvider which coordinates Slot and Fill rendering.

Then, render a Slot component anywhere in your application, giving it a name. The name is either a string or a symbol. Symbol names are useful for slots that are supposed to be private, accessible only to clients that have access to the symbol value.

Any Fill will render its UI in this Slot space, even if rendered elsewhere in the application.

You can either use the Fill component directly, or create a wrapper component (as in the following example) to hide the slot name from the consumer.

import {
    SlotFillProvider,
    Slot,
    Fill,
    Panel,
    PanelBody,
} from '@wordpress/components';

const MySlotFillProvider = () => {
    const MyPanelSlot = () => (
        <Panel header="Panel with slot">
            <PanelBody>
                <Slot name="MyPanelSlot" />
            </PanelBody>
        </Panel>
    );

    MyPanelSlot.Content = () => <Fill name="MyPanelSlot">Panel body</Fill>;

    return (
        <SlotFillProvider>
            <MyPanelSlot />
            <MyPanelSlot.Content />
        </SlotFillProvider>
    );
};

There is also the createSlotFill helper method which was created to simplify the process of matching the corresponding Slot and Fill components:

const { Fill, Slot } = createSlotFill( 'Toolbar' );

const ToolbarItem = () => <Fill>My item</Fill>;

const Toolbar = () => (
    <div className="toolbar">
        <Slot />
    </div>
);

Props

The SlotFillProvider component does not accept any props (except children).

Both Slot and Fill accept a name string prop, where a Slot with a given name will render the children of any associated Fills.

Slot accepts a bubblesVirtually prop which changes the method how the Fill children are rendered. With bubblesVirtually, the Fill is rendered using a React portal. That affects the event bubbling and React context propagation behaviour:

bubblesVirtually=false

  • events will bubble to their parents on the DOM hierarchy (native event bubbling)
  • the React elements inside the Fill will be rendered with React context of the Slot
  • renders the Fill elements directly, inside a Fragment, with no wrapper DOM element

bubblesVirtually=true

  • events will bubble to their virtual (React) parent in the React elements hierarchy
  • the React elements inside the Fill will keep the React context of the Fill and its parents
  • renders a wrapper DOM element inside which the Fill elements are rendered (used as an argument for React createPortal)

Slot with bubblesVirtually=true renders a wrapper DOM element (a div by default) and accepts additional props that customize this element, like className or style. You can also replace the div with another element by passing an as prop.

Slot without bubblesVirtually accepts an optional children prop, which is a function that receives fills array as a param. It allows you to perform additional processing: render a placeholder when there are no fills, or render a wrapper only when there are fills.

Example:

const Toolbar = ( { isMobile } ) => (
    <div className="toolbar">
        <Slot name="Toolbar">
            { ( fills ) => {
                return isMobile && fills.length > 3 ? (
                    <div className="toolbar__mobile-long">{ fills }</div>
                ) : (
                    fills
                );
            } }
        </Slot>
    </div>
);

Additional information (props) can also be passed from a Slot to a Fill by a combination of:
1. Adding a fillProps prop to the Slot.
2. Passing a function as children to the Fill. This function will receive the fillProps as an argument.

const { Fill, Slot } = createSlotFill( 'Toolbar' );

const ToolbarItem = () => (
    <Fill>
        { ( { hideToolbar } ) => {
            <Button onClick={ hideToolbar }>Hide</Button>;
        } }
    </Fill>
);

const Toolbar = () => {
    const hideToolbar = () => {
        console.log( 'Hide toolbar' );
    };
    return (
        <div className="toolbar">
            <Slot fillProps={ { hideToolbar } } />
        </div>
    );
};