DRAFT [2022-2023][ua] at 2023-05-03 17:18:24 +0300
Logo-do [errata] Profile

Frontend-розробка SPA Web-додатків

Знайомство з React. Компоненти

Конспект лекції


14 ЗНАЙОМСТВО З REACT

На даний час веб-додатки проектуються і створюються інакше, ніж 10 років тому. Щоб переконатися в цьому, розглянемо додаток, зображений на рисунку нижче.

Цей додаток являє собою каталог. Він має звичайний набір сторінок, пов'язаних з домашньою сторінкою, сторінка результатів пошуку, сторінка відомостей і т. д. Зараз ми розглянемо два підходи до створення цього додатка.

Багатосторінковий дизайн

Раніше при створенні подібного додатку, застосували б підхід, що передбачає використання декількох окремих сторінок. У цьому випадку потік виглядав би наступним чином.

У відповідь майже на кожну дію, що змінює вміст вікна браузера, веб-додаток переводить вас на зовсім іншу сторінку. Крім того, що знищення та відтворення сторінок погіршує користувальницький досвід, це дуже впливає на спосіб підтримки стану програми. За винятком збереження призначених для користувача даних за допомогою cookie-файлів і деяких серверних механізмів вам нема про що турбуватися.

Односторінкові додатки

Сьогодні модель веб-додатків, яка вимагає переходу між окремими сторінками, застаріває. Замість цього сучасні програми зазвичай грунтуються на так званій моделі SPA (Single-Page Applications, односторінкові додатки). Вона дозволяє створити додаток, в якому ви ніколи не переходьте на інші сторінки і навіть не перезавантажуєте їх. При її використанні різні представлення додатка завантажуються в одній і тій же сторінці. У випадку з нашим додатком це виглядає наступним чином.

В процесі взаємодії користувачів з додатком ми замінюємо вміст області, обведеної червоною пунктирною лінією, даними і HTML-кодом, який відповідає тому, що намагається зробити користувач.

Як односторінкові додатки були створені такі популярні веб-додатки, як Gmail, Facebook, Instagram, Twitter, тощо.

При створенні односторінкових додатків можна зіткнутися з трьома основними проблемами:

Незважаючи на ці недоліки, односторінкові додатки є частиною сьогодення і формують майбутнє розробки веб-додатків.

Бібліотека React

Facebook і Instagram, cпираючись на свій величезний досвід роботи з односторінкового додатками, випустили бібліотеку під назвою React, щоб не тільки усунути описані раніше недоліки, але і змінити сам підхід до розробки односторінкових додатків.

Основні переваги бібліотеки React

  1. Автоматичне управління станом призначеного для користувача інтерфейсу

При роботі з односторінковими додатками відстеження та підтримання стану призначеного для користувача інтерфейсу вимагає великих зусиль і часу. З бібліотекою React вам доведеться турбуватися тільки про кінцевий стан призначеного для користувача інтерфейсу. Не важливо, яким був його початковий стан. Не має значення, які кроки користувачів привели до його зміни. Важливо лише його кінцевий стан.

Бібліотека React визначає, що повинно бути зроблено для правильного представлення призначеного для користувача інтерфейсу, тому все, що стосується управління його станом, більше не є турботою розробника.

  1. Швидке маніпулювання DOM

Оскільки зміна DOM відбувається дуже повільно, при використанні бібліотеки React ви ніколи не будете працювати безпосередньо з DOM. Замість цього ви будете змінювати віртуальну модель DOM, зберігається в пам'яті. Маніпулювання цією віртуальною DOM відбувається надзвичайно швидко, і в потрібний час бібліотека React оновлює реальну DOM. Для цього вона порівнює віртуальну DOM з реальною, виявляючи критично важливі перетворення, і вносить мінімальні зміни, необхідні для підтримки актуальності DOM.

  1. API для створення компонованих інтерфейсів призначених для користувача

Замість того щоб працювати з візуальними елементами додатка як з єдиним монолітним фрагментом, бібліотека React спонукає вас розбивати ці візуальні елементи на більш дрібні компоненти.

Бібліотека React дозволяє працювати з модульними, компактними і автономними елементами. Багато з базових API бібліотеки React спрямовані на полегшення процесу створення невеликих візуальних компонентів, які потім можна об'єднати з іншими візуальними компонентами і створити більш великі і більш складні візуальні компоненти.

  1. Візуальні елементи, цілком визначені в коді JavaScript

Шаблони HTML мають серйозний недолік: вони обмежують спектр дій, які ви можете зробити всередині них, і мова йде не тільки про відображення даних. Наприклад, якщо ви хочете вибрати для відображення фрагмент користувальницького інтерфейсу на підставі певного умови, вам доведеться написати код JavaScript десь в своєму додатку або використовувати якусь специфічну для конкретного фреймворка шаблонну команду.

Бібліотека React робить дещо чудове. Завдяки тому, що призначений для користувача інтерфейс цілком визначений в коді JavaScript, ви можете використовувати широкі функціональні можливості цієї мови для виконання різних дій всередині шаблонів. Ви обмежені лише можливостями JavaScript, а не функціями фреймворка для шаблонів. Бібліотека React дозволяє (при необхідності) визначити візуальні елементи за допомогою HTML-подібного синтаксису JSX, який співіснує з кодом JavaScript:

  1. Тільки V в архітектурі MVC

React не є повноцінним фреймворком, які мають уявлення про те, як повинні поводитися компоненти програми. Замість цього бібліотека React в основному працює в шарі View (представлення), займаючись підтримкою актуальності візуальних елементів. Це означає, що ви можете використовувати все, що завгодно, при роботі над M (моделлю) і C (контролером) своєї архітектури MVC (Model-View-Controller, Модель-Представлення-Контролер). Така гнучкість дозволяє вам вибирати технології, з якими ви вже знайомі, і робить бібліотеку React корисною не тільки для новостворених вами веб-додатків, але і для існуючих додатків, які ви бажаєте покращити, не вдаючись до видалення і рефакторингу коду.

15 СТВОРЕННЯ ПЕРШОГО REACT-ДОДАТКА

Незважаючи на всі переваги React, почати працювати з бібліотекою не так просто.

Робота з JSX

Перш ніж приступити до створення програми, нам слід звернути увагу на один важливий нюанс. React не схожа на попередні бібліотеки JavaScript. Її не дуже радує, коли ви посилаєтеся на написаний для неї код, використовуючи елемент script.

Як ви знаєте, веб-додатки (і все інше, що відображається в браузері) складаються з коду на мовах HTML, CSS і JavaScript.

Неважливо, чи створений веб-додаток за допомогою React або іншої бібліотеки, на зразок Angular, Knockout або jQuery. Кінцевий результат повинен являти собою комбінацію коду на мовах HTML, CSS і JavaScript; в іншому випадку браузер не знатиме, що з ним робити.

Тут і проявляється особливість React. Крім звичайного HTML, CSS і JavaScript основна частина вашого React-коду буде написана з використанням синтаксису JSX. JSX - це мова, яка дозволяє легко змішувати код JavaScript з HTML-подібними тегами для визначення елементів призначеного для користувача інтерфейсу (UI) і їх функціональності. В свою чергу браузер не знає, що робити з JSX. Щоб створити веб-додаток за допомогою бібліотеки React, нам потрібен спосіб перетворення JSX в JavaScript, який браузер здатний зрозуміти.

Якщо ми цього не зробимо, React-додаток не запрацює. Існує два рішення цієї проблеми:

Перше рішення, незважаючи на початкову складність і часові затрати, являє собою сучасний спосіб веб-розробки. Крім компіляції (точніше, транспіляціі) JSX в JS, цей підхід дозволяє вам скористатися перевагами модулів, більш досконалих інструментів збірки і безліччю інших функцій, що полегшують процес створення складних веб-додатків.

Друге рішення - це швидкий і прямий шлях, на якому ви спочатку витрачаєте більше часу на написання коду і менше часу проводите в середовищі розробки. Для його використання вам досить посилатися на файл сценарію. Цей файл сценарію подбає про перетворення JSX в JS при завантаженні сторінки, і додаток React оживе без особливих дій в середовищі розробки з вашого боку.

Для знайомства з React ми використовуємо друге рішення. Справа в тому, що процес конвертації JSX в JS знижує продуктивність браузера. Це цілком прийнятно в процесі вивчення бібліотеки React, але неприпустимо при розгортанні додатків для реального використання.

Використання React

Спочатку створимо порожню HTML-сторінку.

<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React! React! React!</title>
</head>
<body>
<script>
</script>
</body>
</html>

Тепер потрібно додати посилання на бібліотеку React:

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

Необхідно ще додати посилання на компілятор для JavaScript Babel (Babeljs.io).

<script src="https://unpkg.com/[email protected]/babel.min.js"></script>

В нашому випадку нам важлива його здатність перетворювати код JSX в JavaScript.

На даному етапі HTML-сторінка повинна виглядати наступним чином:

<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React! React! React!</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
<script>

</script>
</body>
</html>

Відображення імені

Зараз ви використовуєте бібліотеку React, щоб відобразити на екрані ім'я. Для цього ви застосуєте метод під назвою render. Усередині порожнього елемента script в тілі сторінки додайте наступний код:

ReactDOM.render(
<h1>Шерлок Холмс</h1>,
document.body
);

Перш ніж переглянути сторінку і оцінити результат, вам потрібно позначити цей блок сценарію як щось, з чим може працювати компілятор Babel. Для цього необхідно додати до елементу script атрибут type зі значенням text / babel:

<script type="text/babel">
ReactDOM.render(
<h1>Шерлок Холмс</h1>,
document.body
);
</script>

Після цього можна переглянути результат в браузері.

Метод render приймає два аргументи:

1. HTML-подібні елементи (JSX), які необхідно вивести.

2. Місце в DOM, де React повинна відобразити JSX.

Зміна місця призначення

JavaScript виглядає інакше завдяки JSX, проте в підсумку браузер має справу з чистим кодом на мові HTML, CSS і JavaScript. Щоб переконатися в цьому, ми трохи змінимо поведінку і зовнішній вигляд програми. Спочатку ми змінимо місце для виведення коду JSX.

Використання JavaScript для розміщення контенту безпосередньо в елемент body - це не найкраща ідея. Багато що може піти не так, особливо якщо ви збираєтеся використовувати React поряд з іншими бібліотеками та фреймворками JS. Рекомендується створити окремий елемент, який буде розглядатися як новий кореневий елемент. Цей елемент використаємо в якості місця призначення для методу render. Додамо до коду HTML елемент div з ідентифікатором container:

<body>
<div id="container"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Шерлок Холмс</h1>,
document.body
);
</script>
</body>

Після визначення контейнера div змінимо метод render, щоб використовувати його замість document.body.

ReactDOM.render(
<h1>Шерлок Холмс</h1>,
document.querySelector("#container")
);

Або визначити відповідну змінну за межами метода render:

var destination = document.querySelector("#container");
ReactDOM.render(
<h1>Шерлок Холмс</h1>,
destination
);

Додавання стилів

Усередині елемента head додамо блок style з наступним кодом CSS:

<style>
#container {
padding: 50px;
background-color: #EEE;
}
#container h1 {
font-size: 144px;
font-family: sans-serif;
color: #0080A8;
}
</style>

Неважливо, що елемент h1 повністю визначений в коді JavaScript у вигляді JSX -сінтаксису або що код CSS було визначено поза методом render. Кінцевий результат роботи React-додатку як і раніше буде на 100% складатися з HTML, CSS і JavaScript.

16 КОМПОНЕНТИ REACT

Компоненти – це одна з тих частин, які змушують бібліотеку React реагувати. Вони є одним з основних способів визначення візуальних елементів і взаємодій, з якими мають справу користувачі вашої програми.

У процесі розробки проекту за допомогою бібліотеки React майже кожна частина його візуальних елементів буде знаходитися всередині автономного модуля, що має назву – компонент.

Пунктирними лініями обведені окремі компоненти, які відповідають за зовнішній вигляд і будь-які взаємодії конкретних елементів.

Зміни у взаємодії з призначеним для користувача інтерфейсом

Повернемося до попереднього прикладу і методу render.

var destination = document.querySelector("#container");
ReactDOM.render(
<h1>Шерлок Холмс</h1>,
destination
);

На екрані ви бачите слово Шерлок Холмс, написане великими буквами завдяки елементу h1. Давайте трохи змінимо ситуацію. Припустимо, ми хочемо вивести на екран імена ще кількох героїв. Для цього ми змінимо метод render наступним чином:

var destination = document.querySelector("#container");
ReactDOM.render(
<div>
<h1>Шерлок Холмс</h1>
<h1>Бетмен</h1>
<h1>Термінатор</h1>
<h1>Нео</h1>
</div>,
destination
);

Якщо тепер нам необхідно змінити розмір заголовка, нам доведеться виконати це вручну, що не зовсім зручно. Всі ці зміни будуть також дублюватися для кожного елемента.

Рішення всіх проблем можна знайти в компонентах React. Компоненти React - це багаторазово використовувані фрагменти JavaScript-коду, які виводять HTML-елементи (завдяки JSX). Для прикладу створимо пару компонентів, перед тим створивши чистий HTML документ.

<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React! React! React!</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/babel">

</script>
</body>
</html>

Створимо найпростіший компонент HelloWorld. Якщо використати тільки метод ReactDOM.render ми отримаємо код, який виглядає наступним чином:

ReactDOM.render(
<div>
<p>Привіт світ!</p>
</div>,
document.querySelector("#container")
);

Для створення компонента бібліотека React передбачає кілька способів, проте в даному прикладі ми скористаємося синтаксисом класу.

class HelloWorld extends React.Component {
}

Ми створили новий компонент HelloWorld. Він розширює клас React.Component. Всередину цього класу ви можете помістити всілякі методи для подальшого визначення функцій компонента HelloWorld. Одним з таких незамінних методів є render. Додавши метод render, як показано нижче:

class HelloWorld extends React.Component {
render() {
}
}

Подібно до методу render, який ми розглянули раніше в якості частини ReactDOM.render, функція render всередині компонента також відповідає за взаємодію з кодом JSX. Змінимо функцію render, щоб вивести на екран фразу «Компонент: Привіт світ!».

class HelloWorld extends React.Component {
render() {
return <p>Компонент: Привіт світ!</p>
}
}

Залишається тільки використати цей компонент. Викликати його можна з методу ReactDOM.render.

ReactDOM.render(
<HelloWorld/>,
document.querySelector("#container")
);

Дивлячись на виклик компонента, можна зробити висновок, що над ним можна виконувати і інші HTML-подібні операції:

ReactDOM.render(
<div>
<HelloWorld/>
</div>,
document.querySelector("#container")
);

Ми помістили виклик компонента HelloWorld в елемент div, і якщо ви зараз перегляньте сторінку в своєму браузері, то переконаєтеся в тому, що все працює. Давайте зробимо так, що замість одного виклику HelloWorld зробимо кілька. Змініть метод ReactDOM.render, щоб він виглядав наступним чином:

ReactDOM.render(
<div>
<HelloWorld/>
<HelloWorld/>
<HelloWorld/>
<HelloWorld/>
<HelloWorld/>
<HelloWorld/>
</div>,
document.querySelector("#container")
);

Тепер можна побачити кілька текстових рядків «Компонент: Привіт світ!».

Встановлення властивостей

В даний час наш компонент виконує тільки одну функцію. Він виводить фразу Привіт, світ! на екран. Ця фраза є статичною. Точно також як функція може приймати аргументи, щось подібне можна реалізувати і з компонентами. Але в даному випадку аргументи мають назву ­ властивості.

Спочатку потрібно реалізувати у функції return виведення на екран значення, передане властивістю. Задамо ім'я для властивості; в даному прикладі це буде greetTarget. Щоб вказати значення greetTarget в якості частини нашого компонента, нам потрібно внести наступну зміну:

class HelloWorld extends React.Component {
render() {
return <p>Привіт, {this.props.greetTarget}!</p>
}
}

Ви отримуєте доступ до властивості, посилаючись на неї за допомогою властивості this.props, доступ до якої має кожен компонент. Зверніть увагу на те, як вказується ця властивість: воно заключена в фігурні дужки {і} . У JSX, якщо ви хочете, щоб щось сприймалося як вираз, вам потрібно заключити це в фігурні дужки. Якщо ви цього не зробите, то побачите на екрані текст this.props.greetTarget.

Тепер залишилося передати значення властивості як частину коду виклику компонента. Для цього потрібно додати атрибут з ім'ям властивості, за яким слідує значення, яке ви хочете передати:

ReactDOM.render(
<div>
<HelloWorld greetTarget="Бетмен"/>
<HelloWorld greetTarget="Термінатор"/>
<HelloWorld greetTarget="Нео"/>
<HelloWorld greetTarget="Жінка-кішка"/>
</div>,
document.querySelector("#container")
);

Тепер кожен виклик HelloWorld має атрибут greetTarget. Потрібно зауважити, що ви не обмежені тільки однією властивістю в компоненті. У вас може бути будь-яка кількість властивостей.

Робота з дочірніми елементами

Компоненти як і HTML-елементи можуть мати дочірні елементи.

Наприклад.

<CleverComponent foo="bar">
<p>Clever Component!</p>
</CleverComponent>

У даного компонента є дочірній елемент. До нього ми можемо звернутися за допомогою властивості this.props.children.

Щоб розібратися у всьому цьому, розглянемо інший простий приклад. На цей раз у нас є компонент Buttonify, який поміщає дочірні елементи в елемент button. Цей компонент виглядає наступним чином:

class Buttonify extends React.Component {
render() {
return(
<div>
<button type={this.props.behavior}>{this.props.children}</button>
</div>
);
}
}

Ви можете використовувати цей компонент, викликавши його за допомогою методу ReactDOM.render, як показано нижче:

ReactDOM.render(
<div>
<Buttonify behavior="submit">Надіслати дані</Buttonify>
</div>,
document.querySelector("#container")
);

Зверніть увагу на те, що в код JSX було додано властивість behavior. Вона дозволяє нам вказати атрибут type елемента button, і ви бачите, що ми отримуємо до нього доступ за допомогою властивості this.props.behavior в методі render визначення компонента.

Доступ до дочірніх елементів компонента не обмежується тим, що було представлено. Наприклад, якщо дочірній елемент являє собою простий текст, то властивість this.props.children поверне рядок. Якщо дочірній елемент - одиночний (як в нашому прикладі), то властивість this.props.children поверне один компонент, що не обгорнутий в масив.

17 СТИЛІ В БІБЛІОТЕЦІ REACT

За оформлення HTML-контенту відповідє CSS. Він передбачає гарне розділення вмісту і представлення. Синтаксис селекторів забезпечує більшу гнучкість при прийнятті рішення про те, до яких елементів застосовувати стиль, а до яких - ні.

Бібліотека React має інше уявлення про стилізацію контенту. Вона рекомендує зберігати дані про зовнішній вигляд елементів разом з HTML і JavaScript. Але це лише рекомендації. На практиці ми можемо використовувати обидва підходи.

Розглянемо приклад, який буде відображати на сторінці голосні літери. Давайте додамо пусту HTML-сторінку:

<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Стилі в React</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
<style>
#container {
padding: 50px;
background-color: #FFF;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
</html>

Щоб відобразити на сторінці голосні букви, додамо наступний код:

<script>
var destination = document.querySelector("#container");

class Letter extends React.Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}

ReactDOM.render(
<div>
<Letter>А</Letter>
<Letter>Е</Letter>
<Letter>І</Letter>
<Letter>О</Letter>
<Letter>У</Letter>
</div>,
destination
);
</script>

Ви створюєте компонент під назвою Letter, який відповідає за виведення голосних літер в елемент div.

Стилізуємо наш компонент за допомогою CSS і з використанням нового підходу React.

У нас є кілька компонентів Letter, поміщених в елемент div. Тепер необхідно визначити набір селекторів стилів із зазначенням властивостей. Щоб вплинути на внутрішні елементи div, додайте наступний код в елемент style:

div div div {
padding: 10px;
margin: 10px;
background-color: #FFDE00;
color: #333;
display: inline-block;
font-family: monospace;
font-size: 32px;
text-align: center;
}

Селектор div div div гарантує, що стиль буде застосований до потрібних елементів. В результаті голосні літери будуть стилізовані. Селектор div має дуже загальний характер. У додатках, що містять більше трьох елементів div, стиль може бути застосований не до тих елементів. У таких випадках потрібно призначити внутрішнім елементам div клас letter.

class Letter extends React.Component {
render() {
return (
<div className="letter">
{this.props.children}
</div>
);
}
}

Зверніть увагу, що ми призначаємо клас, використовуючи атрибут className замість атрибута class. Це пов'язано з тим, що слово class є спеціальним ключовим словом в мові JavaScript.

Після присвоєння елементу div атрибута className зі значенням letter, залишилося створити селектор CSS для більш точного опису елементів div:

.letter {
padding: 10px;
margin: 10px;
background-color: #FFDE00;
color: #333;
display: inline-block;
font-family: monospace;
font-size: 32px;
text-align: center;
}

Стилізація контенту методом бібліотеки React

Для бібліотеки React кращим є застосування до контенту вбудованих (в рядок) стилів, тобто підхід, який не передбачає використання CSS. Спочатку це може здатися трохи дивним, проте такий підхід полегшує повторне використання візуальних елементів. Мета полягає в тому, щоб перетворити компоненти в невеликі чорні ящики, де містяться всі дані, що мають відношення до того, як виглядає і функціонує призначений для користувача інтерфейс. Давайте подивимося, як це відбувається.

Давайте повернемо компонент Letter в початковий стан:

class Letter extends React.Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}

Створення об'єкта стилю

Спочатку визначимо об'єкт, що містить стилі, які ми хочемо застосувати:

class Letter extends React.Component {
render() {

var letterStyle = {
padding: 10,
margin: 10,
backgroundColor: "#FFDE00",
color: "#333",
display: "inline-block",
fontFamily: "monospace",
fontSize: 32,
textAlign: "center"
};

return (
<div>
{this.props.children}
</div>
);
}
}

У нас є об'єкт під назвою letterStyle, в якому містяться імена властивостей CSS і їх значення.

Об'єкт letterStyle і його властивості здебільшого представляють собою прямий JavaScript-переклад правила стилю .letter. Тепер залишилося тільки присвоїти цей об'єкт елементу, який ми хочемо стилізувати.

Тепер залишилося застосувати стиль, встановивши атрибут style нашого елемента div .

class Letter extends React.Component {
render() {
var letterStyle = {
padding: 10,
margin: 10,
backgroundColor: "#FFDE00",
color: "#333",
display: "inline-block",
fontFamily: "monospace",
fontSize: 32,
textAlign: "center"
};
return (
<div style={letterStyle}>
{this.props.children}
</div>
);
}
}

Налаштування фонового кольору

В даний час всі голосні літери відображаються на жовтому фоні. Зробимо так, щоб колір фону можна було вказати в рамках кожного оголошення Letter. Щоб реалізувати це спочатку потрібно в методі ReactDOM.render додати атрибут bgcolor і вказати кілька кольорів, як показано в наступних рядках коду:

ReactDOM.render(
<div>
<Letter bgcolor="#58B3FF">А</Letter>
<Letter bgcolor="#FF605F">Е</Letter>
<Letter bgcolor="#FFD52E">І</Letter>
<Letter bgcolor="#49DD8E">О</Letter>
<Letter bgcolor="#AE99FF">У</Letter>
</div>,
destination
);

Тепер ми повинні використати цю властивість. В об'єкті letterStyle задайте this.props.bgColor як значення backgroundColor:

var letterStyle = {
padding: 10,
margin: 10,
backgroundColor: this.props.bgcolor,
color: "#333",
display: "inline-block",
fontFamily: "monospace",
fontSize: 32,
textAlign: "center"
};

18 СТВОРЕННЯ СКЛАДНИХ КОМПОНЕНТІВ

Компоненти в React містять код HTML, JavaScript і стилі, необхідні для запуску Крім можливості повторного використання компоненти привносять ще одну важливу перевагу. Вони надають можливість створювати композиції. Ви можете комбінувати компоненти для створення більш складних.

Розглянемо створення простої картки кольорової палітри.

Використаємо системний підхід, який включає в себе два етапи:

1. Визначити основні візуальні елементи.

2. З'ясувати, які будуть компоненти.

Визначення основних візуальних елементів

Перше, що можна виділити в прикладі, це сама картка. Усередині картки знаходяться дві окремі області. Верхня частина являє собою прямокутну область, яка відображає певний колір. Нижня частина - це біла область з шістнадцятковим значенням кольору.

Головне спочатку визначити важливі візуальні елементи і розбивати їх на структуру «предок / нащадок» до тих пір, поки ви не зможете їх розділяти далі.

Продовжуючи, ми можемо бачити, що пофарбований прямокутник ми не можемо розділити. Але ми можемо відокремити мітку коду кольору від білої області, на якій вона знаходиться.

Визначення компонентів

Тепер потрібно з'ясувати, який з візуальних елементів, які ми визначили, буде перетворений в компонент, а який ні. Не кожен візуальний елемент повинен стати компонентом.

Загальне правило полягає в тому, що компонент повинен робити тільки щось одне. Якщо ви виявите, що ваш потенційний компонент в результаті робить занадто багато, його, ймовірно, потрібно розбити на кілька компонентів. З іншого боку, якщо потенційний компонент має невеликий функціонал, то його, ймовірно, не захочеться використовувати окремо. Давайте з'ясуємо, які елементи складуть компоненти в нашому прикладі. За візуальною ієрархії як картка, так і кольоровий квадрат підходять під частини компонента. Картка виконує роль зовнішнього контейнера, а кольоровий квадрат відображає колір.

Важлива частина – сама мітка. Без неї ми не зможемо побачити шістнадцяткове значення кольору. А біла область має незначну роль. Це лише порожній простір, і її завдання легко може бути передане мітці. Тому на даному етапі ми визначили три компонента.

Створення компонентів

Для початку нам потрібна базова порожня HTML-сторінка, яка послужить відправною точкою:

<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Компоненти в React</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
<style>
#container {
padding: 50px;
background-color: #FFF;
}
</style>
</head>
<body>
<div id="container"></div>
<script script type="text/babel">
var destination = document.querySelector("#container");

ReactDOM.render(
<div>

</div>,
destination
);
</script>
</body>
</html>

Для компонентів будемо використовувати такі імена: Card, Label і Square:

class Square extends React.Component {
render() {
return(
<br/>
);
}
}
class Label extends React.Component {
render() {
return (
<br/>
);
}
}
class Card extends React.Component {
render() {
return (
<br/>
);
}
}

Компонент Card

Давайте почнемо з вершини ієрархії компонентів і зосередимося на компоненті Card. Цей компонент буде діяти як контейнер, в якому будуть знаходитися компоненти Square і Label.

class Card extends React.Component {
render() {
var cardStyle = {
height: 200,
width: 150,
padding: 0,
backgroundColor: "#FFF",
boxShadow: "0px 0px 5px #666"
};
return (
<div style={cardStyle}>

</div>
);
}
}

Щоб побачити компонент Card в дії, нам потрібно відобразити його в моделі DOM в складі функції ReactDOM.render.

ReactDOM.render(
<div>
<Card/>
</div>,
destination
);

Компонент Square

class Square extends React.Component {
render() {
var squareStyle = {
height: 150,
backgroundColor: "#FF6663"
};
return(
<div style={squareStyle}>

</div>
);
}
}

Щоб побачити компонент Square в дії, нам потрібно отримати його в моделі DOM так само, як і компонент Card. Для цього необхідно викликати компонент Square зсередини компонента Card.

class Card extends React.Component {
render() {
var cardStyle = {
height: 200,
width: 150,
padding: 0,
backgroundColor: "#FFF",
boxShadow: "0px 0px 5px #666"
};
return (
<div style={cardStyle}>
<Square />
</div>
);
}
}

Найцікавіше, що ми викликаємо компонент Square зсередини компонента Card. Це приклад компонування компонентів, в якому один компонент пов'язаний з виведенням іншого компонента.

Компонент Label

Останній залишився компонент – Label.

class Label extends React.Component {
render() {
var labelStyle = {
fontFamily: "sans-serif",
fontWeight: "bold",
padding: 13,
margin: 0
};
return (
<p style={labelStyle}>#FF6663</p>
);
}
}

Щоб він в результаті потрапив в DOM, нам потрібно викликати компонент Label через компонент Card.

return (
<div style={cardStyle}>
<Square />
<Label />
</div>
);

Зверніть увагу, що компонент Label розташовується відразу під компонентом Square.

Передача властивостей

У поточному прикладі ми жорстко закодували значення кольору, що використовуються компонентами Square і Label. Це можна вирішити за допомогою передачі значення кольору через властивості. Але дані два компоненти є дочірніми до свого предка компонента Card. Неможливо один раз встановити властивість у компонента предка і отримати до неї доступ із компонента нащадка.

Вірний спосіб передачі значення властивості дочірньому компоненту полягає в тому, щоб кожен проміжний компонент-предок передавав властивість.

class Square extends React.Component {
render() {
var squareStyle = {
height: 150,
backgroundColor: this.props.color
};
return(
<div style={squareStyle}>

</div>
);
}
}
class Label extends React.Component {
render() {
var labelStyle = {
fontFamily: "sans-serif",
fontWeight: "bold",
padding: 13,
margin: 0
};
return (
<p style={labelStyle}>{this.props.color}</p>
);
}
}

class Card extends React.Component {
render() {
var cardStyle = {
height: 200,
width: 150,
padding: 0,
backgroundColor: "#FFF",
boxShadow: "0px 0px 5px #666"
};
return (
<div style={cardStyle}>
<Square color={this.props.color} />
<Label color={this.props.color} />
</div>
);
}
}

ReactDOM.render(
<div>
<Card color="#FF6663" />
</div>,
destination
);

Тепер давайте повернемося до змін, які ми внесли. Незважаючи на те, що властивість color потрібна тільки компонентам Square і Label, компонент-предок Card відповідає за передачу властивості їм. У разі більш глибоко вкладених компонентів використовується більше проміжних компонентів, які будуть відповідати за передачу властивостей. Але є і зворотна сторона медалі. Якщо є кілька властивостей, які потрібно передати компонентам на декількох рівнях, обсяг коду значно збільшуватиметься. Є способи спростити код, і ми розглянемо цей підхід більш докладно в наступному розділі.

19 ПЕРЕДАЧА ВЛАСТИВОСТЕЙ

Передача властивостей з одного компонента в інший - проста процедура, якщо ви маєте справу тільки з одним рівнем вкладеності компонентів. Якщо ж ви хочете передати властивість через кілька рівнів вкладеності, ситуація різко ускладнюється. Давайте розберемося, як ми можемо спростити роботу з властивостями, що передаються через кілька рівнів вкладеності компонентів.

Огляд проблеми

Припустимо, що у нас є глибоко вкладений компонент, і його ієрархія виглядає так, як показано на рисунку нижче:

Нам необхідно передати властивість з червоного кола фіолетовим, де вона буде використовуватися. Напряму нам не вдасться це зробити.

Причина цього в принципах роботи React. React примусово застосовує ланцюжок команд, в якій властивості повинні передаватися від батьківського компонента (предка) до безпосереднього дочірньому компоненту (нащадку). Це означає, що ви не можете пропустити дочірній рівень при передачі властивості.

Це також означає, що нащадки не можуть відправити властивість назад предку. Всі зв'язки однонаправлені - від предка до нащадка. Відповідно до цих рекомендацій передача властивостей з червоного кола в фіолетовий виглядає так, як показано на рисунку нижче.

Кожен компонент, який лежить на наміченому шляху, повинен отримати властивість від свого предка, а потім передати цю властивість своєму нащадкові. Цей процес повторюється, поки властивість не досягне свого пункту призначення. Проблема полягає в етапі прийому і повторної передачі.

Тепер уявіть, що у нас є дві властивості, які нам потрібно відправити, як показано на рисунку нижче.

А як бути, якщо ми хочемо відправити три властивості? Або чотири? Як видно з рисунка, цей підхід не можна масштабувати і вкрай важко підтримувати. Для кожної додаткової властивості ми повинні вказувати зв'язок, ми повинні додавати запис з оголошенням кожного компонента. Якщо ми вирішимо перейменувати властивості, ми повинні переконатися, що кожен екземпляр цієї властивості також перейменований. Якщо ми видалимо властивість, нам потрібно видалити властивість, яка буде використовуватися кожним компонентом, який пов'язаний з нею.

Давайте розглянемо детальний приклад реального коду.

class Display extends React.Component {
render() {
return (
<div>
<p>{this.props.color}</p>
<p>{this.props.num}</p>
<p>{this.props.size}</p>
</div>
);
}
}

class Shirt extends React.Component {
render() {
return (
<div>
<Label color={this.props.color}
num={this.props.num}
size={this.props.size}/>
</div>
);
}
}

class Label extends React.Component {
render() {
return (
<Display color={this.props.color}
num={this.props.num}
size={this.props.size}/>
);
}
}

ReactDOM.render(
<div>
<Shirt color="steelblue" num="3.14" size="medium" />
</div>,
destination
);

У нас є компонент Shirt, який залежний від виведення компонента Label, який, в свою чергу, залежить від виведення компонента Display.

Даний код виведе всього лише три рядки тексту.

Кожен з трьох рядків тексту, які ви бачите, – це властивості, які ми вказали на самому початку коду функції ReactDOM.render:

<Shirt color="steelblue" num="3.14" size="medium" />

Спочатку властивості передаються при виклику компонента Shirt. У компоненті Shirt ці властивості зберігаються всередині об'єкта props. Щоб передати ці властивості, нам потрібно явно отримати доступ до цих властивостей з об'єкта props і перерахувати їх в складі виклику компонента. Нижче наведено приклад, як компонент Shirt викликає компонент Label.

class Shirt extends React.Component {
render() {
return (
<div>
<Label color={this.props.color}
num={this.props.num}
size={this.props.size}/>
</div>
);
}
}

Зверніть увагу, що властивості color, num і size знову перераховані. Єдина відмінність від виклику функції ReactDOM.render полягає в тому, що значення для кожної властивості беруться з їх відповідного запису в об'єкті props, а не вказуються вручну.

Коли компонент Label активується, він має свій об'єкт props, правильно заповнений збереженими значеннями властивостей color, num і size.

У компоненті Display відбуваються всі ті ж кроки.

class Display extends React.Component {
render() {
return (
<div>
<p>{this.props.color}</p>
<p>{this.props.num}</p>
<p>{this.props.size}</p>
</div>
);
}
}

Ми можемо прослідкувати,що кожен компонент по шляху до місця призначення повинен отримати доступ і перевизначити кожну властивість. Це не є гарною практикою.

Оператор розширення

Рішення всіх проблем полягає в новому для JavaScript прийомі, відомому як оператор розширення. Для прикладу розглянемо наступний код.

var items = ["1", "2", "3"];
function printStuff(a, b, c) {
console.log("Вивести: " + a + " " + b + " " + c);
}

Ми хочемо вказати три значення з масиву items в якості аргументів функції printStuff.

printStuff(items[0], items[1], items[2]);

Ми отримуємо доступ до кожного елементу масиву окремо і передаємо його функції printStuff. А з оператором розширення нам доступний більш простий спосіб. Нам не потрібно вказувати кожен елемент масиву окремо:

printStuff(...items);

Оператор розширення дозволяє розгорнути масив на окремі елементи.

При передачі властивостей компонентів ми стикаємося з подібною ситуацією. Усередині компонента об'єкт props виглядає наступним чином:

var props = {
color: "steelblue",
num: "3.14",
size: "medium"
};

© 2006—2023 СумДУ