Публикуем очередную часть перевода учебного курса по React. Нашей сегодняшней темой будут взаимоотношения родительских и дочерних компонентов.
→ Часть 1: обзор курса, причины популярности React, ReactDOM и JSX
→ Часть 2: функциональные компоненты
→ Часть 3: файлы компонентов, структура проектовЗанятие 9. Родительские и дочерние компоненты
Сегодня мы поговорим о родительских и дочерних компонентах. Использование подобных конструкций сделает наше приложение гораздо более сложным, чем в случае, когда в нём был всего один компонент, выводимый в DOM, такой, как
MyInfo
. Вместо этой простой структуры в приложении может присутствовать сложная иерархия компонентов, которая, в итоге, преобразуется в JSX-элементы.
Начнём с уже знакомого вам шаблона приложения. Для того чтобы вспомнить пройденное, можете, по памяти, в пустом файлеindex.js
, написать код для вывода на страницу заголовка первого уровня с текстомHello World!
средствами React. Вот как может выглядеть подобный код:import React from "react"
import ReactDOM from "react-dom"
ReactDOM.render(
<h1>Hello World!</h1>,
document.getElementById("root")
)
В прошлый раз там, где в вышеприведённом коде находится описание элемента
<h1>
, присутствовал код для вывода компонентаMyInfo
. Теперь же мы собираемся создать компонентApp
и вывести его. Для этого нам понадобится код следующего вида:import React from "react"
import ReactDOM from "react-dom"
ReactDOM.render(
<App />,
document.getElementById("root")
)
Компонент
App
будет точкой входа в наше приложение. Вероятно, вы уже заметили, что в коде предыдущего примера кое-чего не хватает. Это действительно так — мы пока не импортировали сюдаApp
. Сделаем это:import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
ReactDOM.render(
<App />,
document.getElementById("root")
)
Но такой код всё ещё остаётся нерабочим. Нам нужен файл компонента
App
(App.js
), расположенный в той же папке, что иindex.js
. Именно к такому файлу мы обращаемся в команде импортаimport App from "./App"
. Напомним, что имена компонентов React записываются в верблюжьем стиле и начинаются с заглавной буквы. Создадим нужный нам файл и опишем в нём компонентApp
. Попытайтесь сделать это самостоятельно. А именно — напишите код, благодаря которому компонентApp
выведет на страницу текстHello again
.
Вот как выглядит этот код:import React from "react"
function App(){
return (
<h1>Hello again</h1>
)
}
export default App
Тут функция
App
возвращает единственный элемент, но напомним, что из подобных функций можно возвращать и более сложные структуры. Самое главное — не забывать о том, что возвратить можно лишь один элемент, который, если, на самом деле, вывести нужно несколько элементов, представляет собой контейнер, включающий их в себя. Например, вот как будет выглядеть возврат разметки, описывающей заголовок первого уровня и маркированный список:import React from "react"
function App(){
return (
<div>
<h1>Hello a third time!</h1>
<ul>
<li>Thing 1</li>
<li>Thing 2</li>
<li>Thing 3</li>
</ul>
</div>
)
}
export default App
Возможно, сейчас мы решим, что то, что формируется средствами компонента
App
, должно представлять собой некий веб-сайт. У него будет навигационный блок (<nav></nav>
) и область основного содержимого (<main></main>
). Это решение приведёт к формированию следующего кода:import React from "react"
function App(){
return (
<div>
<nav>
<h1>Hello a third time!</h1>
<ul>
<li>Thing 1</li>
<li>Thing 2</li>
<li>Thing 3</li>
</ul>
</nav>
<main>
<p>This is where most of my content will go...</p>
</main>
</div>
)
}
export default App
Вот как всё это будет выглядеть в браузере.
Приложение в браузере
Тут ещё можно стилизовать список для того, чтобы он стал больше похожим на навигационную панель.
Можно заметить, что код компонента уже стал довольно большим. Это идёт вразрез с целью, ради которой мы используем React. Ранее мы говорили о том, что фрагменты HTML-кода можно представлять в виде отдельных компонентов, а в нашем компоненте всё свалено в одну кучу. Поэтому сейчас мы создадим компоненты для каждого самостоятельного фрагмента разметки.
Взгляните на эту схему для того, чтобы лучше разобраться в том, о чём идёт речь.Компонент App выводит компонент MyInfo, выводящий элемент <div>
Тут мы выводим на страницу компонент
App
. При этом компонентApp
решает вывести ещё один компонент —MyInfo
. А уже компонентMyInfo
выводит некий JSX-элемент. Обратите внимание на разницу между понятиями «компонент» и «элемент». Элементы — это сущности, которые превращаются в обычный HTML-код. Так, в элементе, представленном в нижней части схемы, используется простой тег<div>
, его имя начинается с маленькой буквы, что говорит нам о том, что это — обычный элемент, а не один из созданных нами компонентов. С другой стороны, имяMyInfo
начинается с большой буквы. Это помогает понять, что перед нами — компонент.
Вы могли слышать о том, что DOM (Document Object Model, объектная модель документа) часто называют «деревом». Корневым элементом этого дерева является элемент<html>
. В нашем случаем корневым элементом дерева, представленного на схеме, является компонентApp
. Возможности этого компонента не ограничиваются выводом другого компонента,MyInfo
в нашем случае. Он может, например, вывести ещё один компонент, представляющий собой «подвал», нижнюю часть страницы. Скажем, этот компонент будет носить имяAwesomeFooter
.Компонент App выводит два компонента
Этот компонент, в свою очередь, может вывести элемент
<footer>
, который будет содержать HTML-код нижней части страницы.
Если у нас имеется «подвал» страницы, то она может содержать и «шапку», оформляющую её верхнюю часть.Компонент App выводит три компонента
Верхняя часть страницы представлена на нашей схеме компонентом
AwesomeHeader
. Такие имена этим компонентам даны для того, чтобы не путать их с элементами. КомпонентAwesomeHeader
, как и компонентApp
, может выводить не только JSX-разметку, но и другие компоненты. Например, это может быть компонентNavBar
, представляющий собой навигационную панель, и компонентLogo
, выводящий логотип. А эти компоненты уже выведут обычные элементы — такие, как<img />
и<nav>
.
По мере рассмотрения этой схемы вы можете заметить, что React-приложение, в ходе его развития, может становиться всё более и более сложным. И то, что мы тут рассмотрели, на самом деле, представляет собой пример крайне простой структуры приложения.
Теперь давайте создадим в нашем учебном приложении компонент, который будет представлять собой «подвал» страницы.
Для этого создадим, в той же папке, где находится файлindex.js
, новый файл. Назовём егоFooter.js
и поместим в него следующий код:import React from "react"
function Footer() {
return (
<footer>
<h3><font color="#3AC1EF">▍This is my footer element</font></h3>
</footer>
)
}
export default Footer
Обратите внимание на то, что имя функционального компонента начинается с большой буквы (
Footer
), а имя элемента(<footer>
) — с маленькой. Как уже было сказано, это помогает отличать элементы от компонентов.
Если теперь обновить страницу, то разметка, формируемая компонентомFooter
, в её нижней части выведена не будет. Это совершенно ожидаемо, так как для того, чтобы её вывести, нужно внести соответствующие изменения в код компонентаApp
.
А именно, речь идёт о том, что в коде файла компонентаApp
нужно импортировать компонентFooter
и создать его экземпляр. Отредактируем код файлаApp.js
:import React from "react"
import Footer from "./Footer"
function App(){
return (
<div>
<nav>
<h1>Hello a third time!</h1>
<ul>
<li>Thing 1</li>
<li>Thing 2</li>
<li>Thing 3</li>
</ul>
</nav>
<main>
<p>This is where most of my content will go...</p>
</main>
<Footer />
</div>
)
}
export default App
Теперь страница, которую формирует приложение, будет выглядеть так, как показано ниже.
Компонент App выводит в нижней части страницы разметку, формируемую другим компонентом — Footer
Можно заметить, что сейчас в коде, выводимом компонентом
App
, имеется странная смесь из обычных JSX-элементов с компонентами. Куда лучше было бы, если бы то, что выводит компонентApp
, было бы похоже на нечто вроде оглавления книги, чтобы в нём присутствовали, в основном, компоненты. А именно, речь идёт о том, чтобы код компонента выглядел бы примерно так:import React from "react"
import Footer from "./Footer"
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
)
}
export default App
Если приложение имеет подобную структуру (в нашем случае, так как файлы компонентов
Header
иMainContent
пока не созданы, код работать не будет), то описание элементов, формирующих различные части страницы, будет находиться в файлах соответствующих компонентов. При этом компоненты, импортируемые в компонентApp
, могут содержать другие вложенные компоненты. Так могут быть сформированы довольно обширные структуры, размеры которых определяются нуждами конкретного приложения.
Здесь мы поговорили о том, как работать с вложенными компонентами. Вы вполне можете попробовать на практике то, что только что узнали, приведя проект, файлApp.js
которого выглядит так, как показано выше, в рабочее состояние.Занятие 10. Практикум. Родительские и дочерние компоненты
▍Задание
Создайте React-приложение с нуля. Выведите на страницу корневой компонент
App
(определённый в отдельном файле). Внутри этого компонента выведите следующие компоненты:
Navbar
MainContent
Footer
Компоненты, выводимые
App
, должны быть описаны в отдельных файлах, каждый из них должен выводить какие-нибудь JSX-элементы.▍Решение
В качестве основы для решения этой задачи используется стандартный проект, создаваемый средствами
create-react-app
.
Код файлаindex.js
:import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
ReactDOM.render(
<App />,
document.getElementById("root")
)
Вот код файла
App.js
. Обратите внимание на то, что для хранения файлов компонентов мы будем использовать папкуcomponents
.import React from "react"
import Header from "./components/Header"
import MainContent from "./components/MainContent"
import Footer from "./components/Footer"
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
)
}
export default App
Код файла
Header.js
:import React from "react"
function Header() {
return (
<header>This is the header</header>
)
}
export default Header
Код файла
MainContent.js
:import React from "react"
function MainContent() {
return (
<main>This is the main section</main>
)
}
export default MainContent
Код файла
Footer.js
:import React from "react"
function Footer() {
return (
<footer>This is the footer</footer>
)
}
export default Footer
Работу с компонентами вы можете организовать так, как вам будет удобнее. То есть, можно, например, сначала написать в файле
App.js
весь необходимый код, выполняющий импорт компонентов и вывод их экземпляров, а потом создать файлы компонентов. Можно сделать всё наоборот — сначала создать файлы компонентов с кодом, а потом уже работать над файломApp.js
. Самое главное, чтобы в итоге получилось работающее приложение.
Вот как выглядит проект в VSCode.Проект в VSCode
А вот как выглядит страница, которую сформировало это приложение.
Страница приложения в браузере
Наше React-приложение работает, но то, что оно выводит на страницу, выглядит как-то совсем неинтересно. Исправить это можно, стилизовав содержимое страницы.
Итоги
Сегодня мы поговорили о родительских и дочерних компонентах. В следующий раз мы начнём работу над нашим первым большим учебным проектом и поговорим о стилизации элементов страницы.
JavaScript