FloatingTree
当嵌套浮动元素在 DOM 上不是彼此的子元素时,为它们提供上下文。
¥Provides context for nested floating elements when they are not children of each other on the DOM.
并非在所有情况下都需要这样做,除非父浮动元素和子浮动元素之间必须有显式通信。这是必要的:
¥This is not necessary in all cases, except when there must be explicit communication between parent and child floating elements. It is necessary for:
-
useDismiss()
Hook 中的bubbles
选项¥The
bubbles
option in theuseDismiss()
Hook -
嵌套的浮动元素在悬停时打开
¥Nested floating elements that each open on hover
-
父子浮动元素之间的自定义通信
¥Custom communication between parent and child floating elements
用法
¥Usage
以下创建了一个无限可嵌套的 <Popover>
组件。该组件的使用必须封装在单个 <FloatingTree>
提供程序中:
¥The following creates an infinitely nestable <Popover>
component. Usage of this component must be wrapped in a single
<FloatingTree>
provider:
import {
FloatingTree,
FloatingNode,
useFloatingNodeId,
} from '@floating-ui/react';
function Popover({children, content}) {
const [isOpen, setIsOpen] = useState(false);
// Subscribe this component to the <FloatingTree> wrapper:
const nodeId = useFloatingNodeId();
// Pass the subscribed `nodeId` to `useFloating`:
const {refs, floatingStyles} = useFloating({
nodeId,
open: isOpen,
onOpenChange: setIsOpen,
});
// Wrap the rendered floating element in a `<FloatingNode>`,
// passing in the subscribed `nodeId`:
return (
<>
{cloneElement(children, {ref: refs.setReference})}
<FloatingNode id={nodeId}>
{isOpen && (
<FloatingPortal>
<div ref={refs.setFloating}>{content}</div>
</FloatingPortal>
)}
</FloatingNode>
</>
);
}
function App() {
return (
<FloatingTree>
<Popover
content={
<Popover content="Nested content">
<button>Nested reference</button>
</Popover>
}
>
<button>Root reference</button>
</Popover>
</FloatingTree>
);
}
钩子
¥Hooks
-
useFloatingNodeId()
将该组件订阅到树上下文。仅调用一次,因为它有副作用。¥
useFloatingNodeId()
subscribes the component to the tree context. Call this only once as it has side effects. -
useFloatingParentNodeId()
返回父FloatingNode
id(如果存在)。这对于根来说是null
(不嵌套)。¥
useFloatingParentNodeId()
returns the parentFloatingNode
id, if it exists. This will benull
for roots (not nested). -
useFloatingTree()
用于访问树对象,其中包括用于跨树组件进行通信的事件触发器 (events
)。¥
useFloatingTree()
for accessing the tree object, which includes an event emitter to communicate across the tree components (events
).
interface FloatingTreeType {
nodesRef: React.MutableRefObject<Array<FloatingNodeType>>;
events: FloatingEvents;
addNode(node: FloatingNodeType): void;
removeNode(node: FloatingNodeType): void;
}
自定义父级
¥Custom parent
默认情况下,父节点是最近的 <FloatingNode />
,但你可以通过传入 parentId
到 useFloatingNodeId()
来指定自定义父节点:
¥By default, the parent node is the closest
<FloatingNode />
, but you can specify a custom parent node
by passing in the parentId
to useFloatingNodeId()
:
const nodeId = useFloatingNodeId(parentId);
如果你想将 React 树中的同级浮动元素标记为另一个同级的子元素,这非常有用。
¥This is useful if you want to mark a sibling floating element in the React tree as the child of another sibling.
<FloatingTree>
封装器
¥<FloatingTree>
wrapper
你可以使用以下技术来避免消费者指定 <FloatingTree>
封装器:
¥You can use the following technique to avoid having the consumer
specify the <FloatingTree>
wrapper:
function PopoverComponent() {
// Main logic as seen earlier
}
// This is the component the consumer uses
export function Popover(props) {
const parentId = useFloatingParentNodeId();
// This is a root, so we wrap it with the tree
if (parentId === null) {
return (
<FloatingTree>
<PopoverComponent {...props} />
</FloatingTree>
);
}
return <PopoverComponent {...props} />;
}
故障排除
¥Troubleshooting
确保你调用 useFloatingNodeId()
的组件是 <FloatingTree>
封装器的子组件。如果不是,那么它将无法找到树上下文。
¥Ensure that the component that you’re calling
useFloatingNodeId()
is a child of the
<FloatingTree>
wrapper. If it is not, then it will not be
able to find the tree context.