При роботі з React час від часу потрібно передавати групи елементів. Раніше для цього ви були змушені додавати непотрібні контейнерні компоненти чи масиви. З появою Фрагментів в React все змінилося: ми отримали просте рішення для групування елементів без зайвої розмітки.
Поглянемо на прикладі:
ReactDOM.render(
<ul>
<React.Fragment>
<li>The fragment element is not rendered to the DOM.</li>
<li>
You can confirm this by right clicking on one of these items
in the preview pane, and inspecting the markup in the browser
dev tools.
</li>
</React.Fragment>
</ul>,
document.querySelector("#root")
)
Компілюється в:
ReactDOM.render(
React.createElement(
"ul",
null,
React.createElement(
React.Fragment,
null,
React.createElement(
"li",
null,
"The fragment element is not rendered to the DOM."
),
React.createElement(
"li",
null,
"You can confirm this by right clicking on one of these items in the preview pane, and inspecting the markup in the browser dev tools."
)
)
),
document.querySelector("#root")
)
<!DOCTYPE html>
<html>
<head>
<title>React Fragments Demo</title>
<script src="https://unpkg.com/react@16.7.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="index.js"></script>
</body>
</html>
React.Fragment
у наведеному прикладі нагадує компонент без розмітки. При рендерингу в DOM, його дочірні компоненти будуть відображатися самі по собі, тобто ми можемо передавати групи елементів без введення непотрібної розмітки. Фрагменти також чудово підходять для роботи з розміткою таблиць та списків (як у прикладі), де просто неможливо ввести додатковий <div>
.
Синтаксис фрагментів
Після тривалого використання фрагментів вас може турбувати незручність: ви марно витрачатимете час на введення <React.Fragment>
, у порівнянні зі звичайним <div>
. І це досить негативно впливає на прийняття фрагментів розробниками. Саме тому з'явився альтернативний стислий синтаксис:
ReactDOM.render(
<>
<h1>Yo Dawg,</h1>
<p>
I heard you like fragments so
I <>put a <>fragment</> in your fragment</>.
</p>
</>,
document.querySelector("#root")
)
Компілюється в:
ReactDOM.render(
React.createElement(
React.Fragment,
null,
React.createElement("h1", null, "Yo Dawg,"),
React.createElement(
"p",
null,
"I heard you like fragments so I ",
React.createElement(
React.Fragment,
null,
"put a ",
React.createElement(
React.Fragment,
null,
"fragment"
),
" in your fragment"
),
"."
)
),
document.querySelector("#root")
)
<!DOCTYPE html>
<html>
<head>
<title>React Fragments Demo</title>
<script src="https://unpkg.com/react@16.7.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="index.js"></script>
</body>
</html>
Як можна побачити, теги <>
та </>
у JSX тепер відповідають <React.Fragment>
та </React.Fragment>
. Більше в них нічого особливого – вони все одно компілюються у тип React.Fragment
.
React.createElement(
React.Fragment,
null,
...childElements
)
З першим релізом фрагменти не отримали особливої підтримки для свого синтаксису. Але з плином часу, більшість популярних інструментів все ж реалізували підтримку:
- Babel 7+
- TypeScript 2.6+
- Prettier 1.9+
- ESLint 3+ (разом з babel-eslint 7)
Вам не треба турбуватись про такі нюанси, якщо ви використовуєте create-react-app: там підтримка фрагментів організована ще з часів їх першого релізу.
Типове застосування
У фрагментів було багато часу, щоб здобути своє місце у реальних кодових базах. У статті оглянемо декілька проектів з відкритим сирцевим кодом, щоб побачити як, зазвичай, фрагменти застосовуються на практиці.
1. Повернення груп елементів
Ви можете використовувати фрагменти для створення компонентів, що повертають список елементів, не огортаючи їх у контейнер чи масив. Такий підхід корисний для компонентів, що повертають розмітку форми та тексту, тому що розмістивши результат в контейнері <div>
, ви матимете головний біль зі стилями.
У сирцевому коді Spectrum часто використовуються фрагменти для таких цілей. Наприклад, компонент для підтвердження електронної пошти:
class UserEmailConfirmation extends React.Component {
render() {
const { emailError, email, isLoading } = this.state;
const { user } = this.props;
return (
<React.Fragment>
<EmailForm
defaultValue={email}
loading={isLoading}
onChange={this.handleEmailChange}
onSubmit={this.init}
style={{ marginTop: '8px', marginBottom: '8px' }}
/>
{user.pendingEmail && (
<Notice>
A confirmation link was sent to {user.pendingEmail}. Click
the confirmation link and then return to this page. You can
resend the confirmation here, or enter a new email address.
</Notice>
)}
{emailError && <Error>{emailError}</Error>}
</React.Fragment>
)
}
}
Примітка: нещодавно GitHub придбав Spectrum, але проект досі з відкритим сирцевим кодом. Огляньте повний код з прикладу на GitHub.
Так само ви можете використовувати фрагменти для повернення груп елементів з функцій рендерингу. Calypso від Wordpress часто використовує такий шаблон, щоб ввести функцію translate
для інтернаціоналізації.
<Step name="my-sites" target="my-sites" placement="below" arrow="top-left">
{ ( { translate } ) => (
<Fragment>
<p>
{ translate(
"{{strong}}First things first.{{/strong}} Up here, you'll " +
"find tools for managing your site's content and design.",
{
components: {
strong: <strong />,
},
}
) }
</p>
<Continue click icon="my-sites" step="sidebar" target="my-sites" />
</Fragment>
) }
</Step>
Примітка: Wordpress імпортує Fragment
з пакета react
і використовує його самостійно, не вказуючи React.Fragment
. Огляньте повний код з прикладу на GitHub.
2. Рендеринг груп елементів за умовою
З фрагментами легше організувати рендеринг груп елементів за певною умовою без зайвої розмітки. Наприклад, можна використати тернарний оператор, щоб відобразити потрібний вміст елементу в залежності від значення prop:
export function ResetPasswordScreen({ hasPassword, name, ...formProps }) {
return (
<Layout>
{hasPassword ? (
<>
<h3>Reset your password</h3>
<p>
To finish resetting your password, enter your new password
here and hit the "Reset" button.
</p>
</>
) : (
<>
<h3>Hi {name}!</h3>
<p>Thanks for joining!</p>
<p>
Just one more step - please pick a password for your account:
</p>
</>
)}
<ResetPasswordForm {...formProps} />
</Layout>
)
}
Фрагменти з масивами
Наостанок оглянемо ще одну потужну функцію фрагментів, яка використовується рідко. Але для особливих випадків вона безцінна.
У фрагментах можуть бути key
props!
<React.Fragment key='somevalue'>
...
</React.Fragment>
Примітка: щоб для фрагмента визначити key
, вам треба використати стандартний синтаксис елементу JSX, тут не підійде новий синтаксис <></>
.
Навіщо визначати key
для фрагмента? Так ви зможете використати фрагменти як елементи масивів, а отже з'єднувати їх у текст. І все це без непотрібної розмітки.
Наприклад, у консольному представленні редактора Demoboard від Frontend Armory використано фрагменти з key
, щоб замінити символи нового рядка на елемент <br>
. Отримали компактний код з широким функціоналом!
Поглянемо на практиці як можна використати фрагменти React для рендерингу символу нового рядка як тег <br>
без зайвої розмітки:
var message =
"\
Hello, there!\
HTML doesn't render new line characters by default,\
but you can add them back in as <br> tags!\
"
// Треба замінити символи переходу рядка
// на елементи `<br>`.
var messageWithLineBreaks = message
ReactDOM.render(
React.createElement("div", null, messageWithLineBreaks),
document.querySelector("#root")
<!DOCTYPE html>
<html>
<head>
<title>React Fragments Demo</title>
<script src="https://unpkg.com/react@16.7.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="index.js"></script>
</body>
</html>
Рішення:
let message = `
Hello, there!
HTML doesn't render new line characters by default,
but you can add them back in as <br> tags!
`
let messageWithLineBreaks =
message.split('\
').map((line, i, lines) =>
<React.Fragment key={i}>
{line}
{i !== lines.length - 1 && <br />}
</React.Fragment>
)
ReactDOM.render(
<div>{messageWithLineBreaks}</div>,
document.querySelector('#root')
)
Ще немає коментарів