<Fragment> 通常使用 <>...</> 代替,它们都允许你在不添加额外节点的情况下将子元素组合。
<>
<OneChild />
<AnotherChild />
</>参考
<Fragment>
当你需要单个元素时,你可以使用 <Fragment> 将其他元素组合起来,使用 <Fragment> 组合后的元素不会对 DOM 产生影响,就像元素没有被组合一样。在大多数情况下,<Fragment></Fragment> 可以简写为空的 JSX 元素 <></>。
参数
- 可选
key:列表中<Fragment>的可以拥有 keys。 - Canary only optional
ref: A ref object (e.g. fromuseRef) or callback function. React provides aFragmentInstanceas the ref value that implements methods for interacting with the DOM nodes wrapped by the Fragment.
Canary only FragmentInstance
When you pass a ref to a fragment, React provides a FragmentInstance object with methods for interacting with the DOM nodes wrapped by the fragment:
Event handling methods:
addEventListener(type, listener, options?): Adds an event listener to all first-level DOM children of the Fragment.removeEventListener(type, listener, options?): Removes an event listener from all first-level DOM children of the Fragment.dispatchEvent(event): Dispatches an event to a virtual child of the Fragment to call any added listeners and can bubble to the DOM parent.
Layout methods:
compareDocumentPosition(otherNode): Compares the document position of the Fragment with another node.- If the Fragment has children, the native
compareDocumentPositionvalue is returned. - Empty Fragments will attempt to compare positioning within the React tree and include
Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC. - Elements that have a different relationship in the React tree and DOM tree due to portaling or other insertions are
Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC.
- If the Fragment has children, the native
getClientRects(): Returns a flat array ofDOMRectobjects representing the bounding rectangles of all children.getRootNode(): Returns the root node containing the Fragment’s parent DOM node.
Focus management methods:
focus(options?): Focuses the first focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.focusLast(options?): Focuses the last focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.blur(): Removes focus ifdocument.activeElementis within the Fragment.
Observer methods:
observeUsing(observer): Starts observing the Fragment’s DOM children with an IntersectionObserver or ResizeObserver.unobserveUsing(observer): Stops observing the Fragment’s DOM children with the specified observer.
注意事项
-
如果你要传递
key给一个<Fragment>,你不能使用<>...</>,你必须从'react'中导入Fragment且表示为<Fragment key={yourKey}>...</Fragment>。 -
当你要从
<><Child /></>转换为[<Child />]或<><Child /></>转换为<Child />,React 并不会重置 state。这个规则只在一层深度的情况下生效,如果从<><><Child /></></>转换为<Child />则会重置 state。在这里查看更详细的介绍。 -
Canary only If you want to pass
refto a Fragment, you can’t use the<>...</>syntax. You have to explicitly importFragmentfrom'react'and render<Fragment ref={yourRef}>...</Fragment>.
用法
返回多个元素
使用 Fragment 或简写语法 <>...</> 将多个元素组合在一起,你可以使用它将多个元素等效于单个元素。例如,一个组件只能返回一个元素,但是可以使用 Fragment 将多个元素组合在一起,并作为一个元素返回:
function Post() {
return (
<>
<PostTitle />
<PostBody />
</>
);
}Fragment 作用很大,它与将元素包裹在一个 DOM 容器中不同,使用 Fragment 对元素进行组合后不会影响布局和样式。如果你使用浏览器调试工具查看这个示例,你会发现所有的 <h1> 和 <article> DOM 节点都是没有父元素的兄弟节点:
export default function Blog() { return ( <> <Post title="An update" body="It's been a while since I posted..." /> <Post title="My new blog" body="I am starting a new blog!" /> </> ) } function Post({ title, body }) { return ( <> <PostTitle title={title} /> <PostBody body={body} /> </> ); } function PostTitle({ title }) { return <h1>{title}</h1> } function PostBody({ body }) { return ( <article> <p>{body}</p> </article> ); }
深入探讨
这个示例从 React 中导入了 Fragment:
import { Fragment } from 'react';
function Post() {
return (
<Fragment>
<PostTitle />
<PostBody />
</Fragment>
);
}通常你不需要这样,除非你需要将 key 传递给 Fragment。
分配多个元素给一个变量
和其他元素一样,你可以将 Fragment 元素分配给变量,作为 props 传递等:
function CloseDialog() {
const buttons = (
<>
<OKButton />
<CancelButton />
</>
);
return (
<AlertDialog buttons={buttons}>
Are you sure you want to leave this page?
</AlertDialog>
);
}组合文本与组件
你可以使用 Fragment 将文本与组件组合在一起:
function DateRangePicker({ start, end }) {
return (
<>
From
<DatePicker date={start} />
to
<DatePicker date={end} />
</>
);
}渲染 Fragment 列表
在这种情况下,你需要显式地表示为 Fragment,而不是使用简写语法 <></>。当你在循环中渲染多个元素时,你需要为每一个元素分配一个 key。如果这个元素为 Fragment 时,则需要使用普通的 JSX 语法来提供 key 属性。
function Blog() {
return posts.map(post =>
<Fragment key={post.id}>
<PostTitle title={post.title} />
<PostBody body={post.body} />
</Fragment>
);
}你可以查看 DOM 以验证组合后的子元素实际上并没有父元素 Fragment:
import { Fragment } from 'react'; const posts = [ { id: 1, title: 'An update', body: "It's been a while since I posted..." }, { id: 2, title: 'My new blog', body: 'I am starting a new blog!' } ]; export default function Blog() { return posts.map(post => <Fragment key={post.id}> <PostTitle title={post.title} /> <PostBody body={post.body} /> </Fragment> ); } function PostTitle({ title }) { return <h1>{title}</h1> } function PostBody({ body }) { return ( <article> <p>{body}</p> </article> ); }
Canary only Using Fragment refs for DOM interaction
Fragment refs allow you to interact with the DOM nodes wrapped by a Fragment without adding extra wrapper elements. This is useful for event handling, visibility tracking, focus management, and replacing deprecated patterns like ReactDOM.findDOMNode().
import { Fragment } from 'react';
function ClickableFragment({ children, onClick }) {
return (
<Fragment ref={fragmentInstance => {
fragmentInstance.addEventListener('click', handleClick);
return () => fragmentInstance.removeEventListener('click', handleClick);
}}>
{children}
</Fragment>
);
}Canary only Tracking visibility with Fragment refs
Fragment refs are useful for visibility tracking and intersection observation. This enables you to monitor when content becomes visible without requiring the child Components to expose refs:
import { Fragment, useRef, useLayoutEffect } from 'react';
function VisibilityObserverFragment({ threshold = 0.5, onVisibilityChange, children }) {
const fragmentRef = useRef(null);
useLayoutEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
onVisibilityChange(entries.some(entry => entry.isIntersecting))
},
{ threshold }
);
fragmentRef.current.observeUsing(observer);
return () => fragmentRef.current.unobserveUsing(observer);
}, [threshold, onVisibilityChange]);
return (
<Fragment ref={fragmentRef}>
{children}
</Fragment>
);
}
function MyComponent() {
const handleVisibilityChange = (isVisible) => {
console.log('Component is', isVisible ? 'visible' : 'hidden');
};
return (
<VisibilityObserverFragment onVisibilityChange={handleVisibilityChange}>
<SomeThirdPartyComponent />
<AnotherComponent />
</VisibilityObserverFragment>
);
}This pattern is an alternative to Effect-based visibility logging, which is an anti-pattern in most cases. Relying on Effects alone does not guarantee that the rendered Component is observable by the user.
Canary only Focus management with Fragment refs
Fragment refs provide focus management methods that work across all DOM nodes within the Fragment:
import { Fragment, useRef } from 'react';
function FocusFragment({ children }) {
return (
<Fragment ref={(fragmentInstance) => fragmentInstance?.focus()}>
{children}
</Fragment>
);
}The focus() method focuses the first focusable element within the Fragment, while focusLast() focuses the last focusable element.