<Fragment> (<>...</>)
<Fragment>๋ <>...</> ๋ฌธ๋ฒ์ผ๋ก ์์ฃผ ์ฌ์ฉ๋๋ฉฐ, ๋ํผ ๋
ธ๋ ์์ด ์๋ฆฌ๋จผํธ๋ฅผ ๊ทธ๋ฃนํํ ์ ์๊ฒ ํด์ค๋๋ค.
<>
<OneChild />
<AnotherChild />
</>๋ ํผ๋ฐ์ค
<Fragment>
ํ๋์ ์๋ฆฌ๋จผํธ๊ฐ ํ์ํ ์ํฉ์์ ์๋ฆฌ๋จผํธ๋ฅผ <Fragment>๋ก ๊ฐ์ธ์ ๊ทธ๋ฃนํํ์ธ์. Fragment ์์์ ๊ทธ๋ฃนํ๋ ์๋ฆฌ๋จผํธ๋ DOM ๊ฒฐ๊ณผ๋ฌผ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. ์ฆ, ์๋ฆฌ๋จผํธ๊ฐ ๊ทธ๋ฃนํ๋์ง ์์ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋น JSX ํ๊ทธ์ธ <></>๋ <Fragment></Fragment>์ ์ถ์ฝํ์
๋๋ค.
Props
-
optional
key: ๋ช ์์ <Fragment>๋ก ์ ์ธ๋Fragment์๋key๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. -
Canary only optional
ref: ref ๊ฐ์ฒด(์:useRef์์ ๋ฐํ๋ ๊ฒ) ๋๋ ์ฝ๋ฐฑ ํจ์์ ๋๋ค. React๋Fragment๋ก ๊ฐ์ผ DOM ๋ ธ๋์ ์ํธ์์ฉํ๊ธฐ ์ํ ๋ฉ์๋๋ฅผ ๊ตฌํํFragmentInstance๋ฅผ ref ๊ฐ์ผ๋ก ์ ๊ณตํฉ๋๋ค.
Canary only FragmentInstance
Fragment์ ref๋ฅผ ์ ๋ฌํ๋ฉด, React๋ Fragment๋ก ๊ฐ์ผ DOM ๋
ธ๋์ ์ํธ์์ฉํ๊ธฐ ์ํ ๋ฉ์๋๊ฐ ํฌํจ๋ FragmentInstance ๊ฐ์ฒด๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฉ์๋
addEventListener(type, listener, options?): Fragment์ ๋ชจ๋ ์ต์์ DOM ์์์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.removeEventListener(type, listener, options?): Fragment์ ๋ชจ๋ ์ต์์ DOM ์์์์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํฉ๋๋ค.dispatchEvent(event): Fragment์ ๊ฐ์ ์์์ ์ด๋ฒคํธ๋ฅผ ๋์คํจ์นํ์ฌ ์ถ๊ฐ๋ ๋ฆฌ์ค๋๋ฅผ ํธ์ถํ๋ฉฐ, DOM ๋ถ๋ชจ๋ก ๋ฒ๋ธ๋ง๋ ์ ์์ต๋๋ค.
๋ ์ด์์ ๋ฉ์๋
compareDocumentPosition(otherNode): Fragment์ ๋ฌธ์ ์์น๋ฅผ ๋ค๋ฅธ ๋ ธ๋์ ๋น๊ตํฉ๋๋ค.- Fragment์ ์์์ด ์์ผ๋ฉด ๋ค์ดํฐ๋ธ
compareDocumentPosition๊ฐ์ด ๋ฐํ๋ฉ๋๋ค. - ๋น Fragment๋ React ํธ๋ฆฌ ๋ด์์ ์์น๋ฅผ ๋น๊ตํ๋ฉฐ
Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC์ ํฌํจํฉ๋๋ค. - ํฌํ์ด๋ ๋ค๋ฅธ ์ฝ์
์ผ๋ก ์ธํด React ํธ๋ฆฌ์ DOM ํธ๋ฆฌ์์ ๋ค๋ฅธ ๊ด๊ณ๋ฅผ ๊ฐ์ง ์๋ฆฌ๋จผํธ๋
Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC์ ๋๋ค.
- Fragment์ ์์์ด ์์ผ๋ฉด ๋ค์ดํฐ๋ธ
getClientRects(): ๋ชจ๋ ์์์ ๊ฒฝ๊ณ ์ฌ๊ฐํ์ ๋ํ๋ด๋DOMRect๊ฐ์ฒด์ ํํํ๋ ๋ฐฐ์ด์ ๋ฐํํฉ๋๋ค.getRootNode(): Fragment์ ๋ถ๋ชจ DOM ๋ ธ๋๋ฅผ ํฌํจํ๋ ๋ฃจํธ ๋ ธ๋๋ฅผ ๋ฐํํฉ๋๋ค.
ํฌ์ปค์ค ๊ด๋ฆฌ ๋ฉ์๋
focus(options?): Fragment ๋ด์ ์ฒซ ๋ฒ์งธ ํฌ์ปค์ค ๊ฐ๋ฅํ DOM ๋ ธ๋์ ํฌ์ปค์คํฉ๋๋ค. ์ค์ฒฉ๋ ์์์ ๋ํด ๊น์ด ์ฐ์ ์ผ๋ก ํฌ์ปค์ค๋ฅผ ์๋ํฉ๋๋ค.focusLast(options?): Fragment ๋ด์ ๋ง์ง๋ง ํฌ์ปค์ค ๊ฐ๋ฅํ DOM ๋ ธ๋์ ํฌ์ปค์คํฉ๋๋ค. ์ค์ฒฉ๋ ์์์ ๋ํด ๊น์ด ์ฐ์ ์ผ๋ก ํฌ์ปค์ค๋ฅผ ์๋ํฉ๋๋ค.blur():document.activeElement๊ฐ Fragment ๋ด์ ์์ผ๋ฉด ํฌ์ปค์ค๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
์ต์ ๋ฒ ๋ฉ์๋
observeUsing(observer): IntersectionObserver ๋๋ ResizeObserver๋ก Fragment์ DOM ์์์ ๊ด์ฐฐํ๊ธฐ ์์ํฉ๋๋ค.unobserveUsing(observer): ์ง์ ๋ ์ต์ ๋ฒ๋ก Fragment์ DOM ์์ ๊ด์ฐฐ์ ์ค์งํฉ๋๋ค.
์ฃผ์ ์ฌํญ
-
Fragment์
key๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด<>...</>๊ตฌ๋ฌธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ช ์์ ์ผ๋กreact์์Fragment๋ฅผ ๋ถ๋ฌ์ค๊ณ Import<Fragment key={yourKey}>...</Fragment>๋ฅผ ๋ ๋๋งํด์ผ ํฉ๋๋ค. -
React๋
<><Child /></>์์[<Child />]๋ก ๋ ๋๋งํ๊ฑฐ๋ (๋๋ ๋ฐ๋์ ๊ฒฝ์ฐ), ํน์<><Child /></>์์<Child />๋ ๋๋งํ๊ฑฐ๋ (๋๋ ๋ฐ๋์ ๊ฒฝ์ฐ) State๋ฅผ ์ด๊ธฐํํ์ง ์์ต๋๋ค. ์ด๋ ์ค์ง ํ ๋จ๊ณ ๊น์ดSingle Level Deep๊น์ง๋ง ์ ์ฉ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด<><><Child /></></>์์<Child />๋ก ๋ ๋๋งํ๋ ๊ฒ์ State๊ฐ ์ด๊ธฐํ๋ฉ๋๋ค. ์ ํํ ์๋ฏธ๋ ์ฌ๊ธฐ์ ํ์ธํ ์ ์์ต๋๋ค. -
Canary only Fragment์
ref๋ฅผ ์ ๋ฌํ๋ ค๋ฉด<>...</>๋ฌธ๋ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ช ์์ ์ผ๋ก'react'์์Fragment๋ฅผ ๋ถ๋ฌ์ค๊ณ<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 ๊ฒ๊ณผ ๋์ผํฉ๋๋ค.
import { Fragment } from 'react';
function Post() {
return (
<Fragment>
<PostTitle />
<PostBody />
</Fragment>
);
}์ผ๋ฐ์ ์ผ๋ก Fragment์ key๋ฅผ ๋๊ฒจ์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ์ด ๊ธฐ๋ฅ์ ํ์ํ์ง ์์ต๋๋ค.
๋ณ์์ ์ฌ๋ฌ ์๋ฆฌ๋จผํธ ํ ๋น
๋ค๋ฅธ ์๋ฆฌ๋จผํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก 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์ธ ๊ฒฝ์ฐ key ์์ฑ์ ์ ๊ณตํ๊ธฐ ์ํด ์ผ๋ฐ JSX ์๋ฆฌ๋จผํธ ๋ฌธ๋ฒ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
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 Fragment ref๋ฅผ ์ฌ์ฉํ DOM ์ํธ์์ฉ
Fragment ref๋ฅผ ์ฌ์ฉํ๋ฉด ๋ํผ ์๋ฆฌ๋จผํธ๋ฅผ ์ถ๊ฐํ์ง ์๊ณ ๋ Fragment๋ก ๊ฐ์ผ DOM ๋
ธ๋์ ์ํธ์์ฉํ ์ ์์ต๋๋ค. ์ด๋ฒคํธ ์ฒ๋ฆฌ, ๊ฐ์์ฑ ์ถ์ , ํฌ์ปค์ค ๊ด๋ฆฌ, ๊ทธ๋ฆฌ๊ณ 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 Fragment ref๋ก ๊ฐ์์ฑ ์ถ์ ํ๊ธฐ
Fragment ref๋ ๊ฐ์์ฑ ์ถ์ ๊ณผ ๊ต์ฐจ ๊ด์ฐฐ์ ์ ์ฉํฉ๋๋ค. ์์ ์ปดํฌ๋ํธ๊ฐ ref๋ฅผ ๋ ธ์ถํ์ง ์์๋ ์ฝํ ์ธ ๊ฐ ํ๋ฉด์ ๋ณด์ด๋ ์์ ์ ๋ชจ๋ํฐ๋งํ ์ ์์ต๋๋ค.
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>
);
}์ด ํจํด์ Effect ๊ธฐ๋ฐ ๊ฐ์์ฑ ๋ก๊น ์ ๋์์ด๋ฉฐ, Effect ๊ธฐ๋ฐ ๋ฐฉ์์ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ํฐํจํด์ ๋๋ค. Effect์๋ง ์์กดํ๋ฉด ๋ ๋๋ง๋ ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ์์๊ฒ ์ค์ ๋ก ๋ณด์ด๋์ง ๋ณด์ฅํ ์ ์์ต๋๋ค.
Canary only Fragment ref๋ก ํฌ์ปค์ค ๊ด๋ฆฌํ๊ธฐ
Fragment ref๋ Fragment ๋ด์ ๋ชจ๋ DOM ๋ ธ๋์์ ๋์ํ๋ ํฌ์ปค์ค ๊ด๋ฆฌ ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
import { Fragment, useRef } from 'react';
function FocusFragment({ children }) {
return (
<Fragment ref={(fragmentInstance) => fragmentInstance?.focus()}>
{children}
</Fragment>
);
}focus() ๋ฉ์๋๋ Fragment ๋ด์ ์ฒซ ๋ฒ์งธ ํฌ์ปค์ค ๊ฐ๋ฅํ ์๋ฆฌ๋จผํธ์ ํฌ์ปค์คํ๊ณ , focusLast()๋ ๋ง์ง๋ง ํฌ์ปค์ค ๊ฐ๋ฅํ ์๋ฆฌ๋จผํธ์ ํฌ์ปค์คํฉ๋๋ค.