Главная » Хабрахабр » [Перевод] Учебный курс по React, часть 9: свойства компонентов

[Перевод] Учебный курс по React, часть 9: свойства компонентов

В сегодняшней части перевода учебного курса по React мы поговорим о свойствах компонентов. Это — одна из важнейших концепций, нашедших отражение в данной библиотеке.

image

→ Часть 1: обзор курса, причины популярности React, ReactDOM и JSX
→ Часть 2: функциональные компоненты
→ Часть 3: файлы компонентов, структура проектов
→ Часть 4: родительские и дочерние компоненты
→ Часть 5: начало работы над TODO-приложением, основы стилизации
→ Часть 6: о некоторых особенностях курса, JSX и JavaScript
→ Часть 7: встроенные стили
→ Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов
→ Часть 9: свойства компонентов

Занятие 19. Свойства компонентов в React

→ Оригинал

Создадим новый проект средствами create-react-app и изменим код нескольких стандартных файлов из папки src.

Вот код файла index.js:

import React from "react"
import ReactDOM from "react-dom"
import "./index.css" import App from "./App" ReactDOM.render(<App />, document.getElementById("root"))

Вот стили, которые описаны в файле index.css:

body { margin: 0;
} .contacts { display: flex; flex-wrap: wrap;
} .contact-card { flex-basis: 250px; margin: 20px;
} .contact-card > img { width: 100%; height: auto;
} .contact-card > h3 { text-align: center;
} .contact-card > p { font-size: 12px;
}

Вот код, находящийся в файле App.js:

import React from "react" function App() { return ( <div className="contacts"> <div className="contact-card"> <img align="center" src="http://placekitten.com/300/200"/> <h3><font color="#3AC1EF">▍Mr. Whiskerson</font></h3> <p>Phone: (212) 555-1234</p> <p>Email: mr.whiskaz@catnap.meow</p> </div> <div className="contact-card"> <img align="center" src="http://placekitten.com/400/200"/> <h3><font color="#3AC1EF">▍Fluffykins</font></h3> <p>Phone: (212) 555-2345</p> <p>Email: fluff@me.com</p> </div> <div className="contact-card"> <img align="center" src="http://placekitten.com/400/300"/> <h3><font color="#3AC1EF">▍Destroyer</font></h3> <p>Phone: (212) 555-3456</p> <p>Email: ofworlds@yahoo.com</p> </div> <div className="contact-card"> <img align="center" src="http://placekitten.com/200/100"/> <h3><font color="#3AC1EF">▍Felix</font></h3> <p>Phone: (212) 555-4567</p> <p>Email: thecat@hotmail.com</p> </div> </div> )
} export default App

Вот как будет выглядеть это приложение в браузере.

Страница приложения в браузере

Сейчас эти элементы формируются средствами компонента App. Проанализировав код и внешний вид приложения можно прийти к выводу о том, что для вывода карточек со сведениями о животных хорошо было бы использовать особые компоненты. Учитывая же то, о чём мы говорили на предыдущих занятиях, можно пойти и дальше — подумать об универсальном компоненте, который можно настраивать, передавая ему атрибуты или свойства.

Для того чтобы создать компонент, который в дальнейшем станет основой для всех подобных карточек, можно взять один из фрагментов разметки, возвращаемой компонентом App. В нашем приложении имеются карточки с изображениями кошек, их именами и контактными сведениями их владельцев (а может — и их самих) — телефоном и адресом электронной почты. Например — такой:

<div className="contact-card"> <img align="center" src="http://placekitten.com/300/200"/> <h3><font color="#3AC1EF">▍Mr. Whiskerson</font></h3> <p>Phone: (212) 555-1234</p> <p>Email: mr.whiskaz@catnap.meow</p>
</div>

App возвращает четыре подобных блока, каждый из них можно было бы использовать для создания самостоятельного компонента, но такой подход нас не устраивает. Поэтому создадим один компонент, который станет основой всех карточек, выводимых приложением. Для этого создадим в папке src новый файл компонента — ContactCard.js и поместим в него код, который возвращает первый элемент <div>, возвращаемый компонентом App, код которого приведён выше. Вот каким будет код компонента ContactCard:

import React from "react" function ContactCard() { return ( <div className="contact-card"> <img align="center" src="http://placekitten.com/300/200"/> <h3><font color="#3AC1EF">▍Mr. Whiskerson</font></h3> <p>Phone: (212) 555-1234</p> <p>Email: mr.whiskaz@catnap.meow</p> </div> )
} export default ContactCard

Ясно, что если создать несколько экземпляров этого компонента, то все они будут содержать одни и те же данные, так как эти данные жёстко заданы в коде компонента. А нам хотелось бы, чтобы, при создании разных экземпляров этого компонента, можно было бы настраивать выводимые им данные. Речь идёт о том, чтобы компоненту можно было бы передавать некие свойства, которыми он потом сможет воспользоваться.

Как известно, функции могут принимать аргументы, хотя их можно использовать и без аргументов. Мы работаем с функциональными компонентами, которые представляют собой обычные JS-функции, в которых, благодаря использованию библиотеки React, можно использовать особые конструкции. Аналогией нашего компонента ContactCard, в том виде, в котором он сейчас существует, может стать такая вот простая функция, которая, ничего не принимая, просто возвращает сумму двух чисел:

function addNumbers() { return 1 + 1
}

Её можно использовать для того, чтобы узнать сумму чисел 1 и 1, но, например, для того, чтобы сложить 1 и 2, используя функции, которые не принимают никаких входных данных, нам пришлось бы писать новую функцию. Совершенно очевидно то, что такой подход приведёт к огромным неудобствам при необходимости сложения разных чисел, поэтому в подобной ситуации будет разумным создать универсальную функцию для сложения чисел, которая принимает два числа и возвращает их сумму:

function addNumbers(a, b) { return a + b
}

То, что возвращает такая функция, будет зависеть от того, какие аргументы ей передали при вызове. Создавая React-компоненты мы можем пойти точно таким же путём.

Импортируем в файл App.js компонент ContactCard и вернём четыре его экземпляра, не удаляя пока код, который формирует карточки на странице приложения:

import React from "react"
import ContactCard from "./ContactCard" function App() { return ( <div className="contacts"> <ContactCard /> <ContactCard /> <ContactCard /> <ContactCard /> <div className="contact-card"> <img align="center" src="http://placekitten.com/300/200"/> <h3><font color="#3AC1EF">▍Mr. Whiskerson</font></h3> <p>Phone: (212) 555-1234</p> <p>Email: mr.whiskaz@catnap.meow</p> </div> <div className="contact-card"> <img align="center" src="http://placekitten.com/400/200"/> <h3><font color="#3AC1EF">▍Fluffykins</font></h3> <p>Phone: (212) 555-2345</p> <p>Email: fluff@me.com</p> </div> <div className="contact-card"> <img align="center" src="http://placekitten.com/400/300"/> <h3><font color="#3AC1EF">▍Destroyer</font></h3> <p>Phone: (212) 555-3456</p> <p>Email: ofworlds@yahoo.com</p> </div> <div className="contact-card"> <img align="center" src="http://placekitten.com/200/100"/> <h3><font color="#3AC1EF">▍Felix</font></h3> <p>Phone: (212) 555-4567</p> <p>Email: thecat@hotmail.com</p> </div> </div> )
} export default App

Теперь поработаем над кодом, используемым для создания экземпляров компонента ContactCard. Создавая обычные HTML-элементы, мы можем настраивать их атрибуты, влияющие на их поведение и внешний вид. Имена этих атрибутов жёстко заданы стандартом. В случае с компонентами можно воспользоваться точно таким же подходом, с той только разницей, что имена атрибутов мы придумываем сами, и сами же решаем — как именно они будут использованы в коде компонента.

Это — изображение кошки и её имя, а также телефон и адрес электронной почты. Каждая из карточек содержит четыре фрагмента информации, которые, от карточки к карточке, могут меняться. Пусть имя кошки будет содержаться в свойстве name, адрес изображения — в свойстве imgURL, телефон — в свойстве phone, а адрес электронной почты — в свойстве email.

В результате код компонента App будет выглядеть так: Зададим эти свойства экземплярам компонентов ContactCard и, по мере переноса данных из кода, который уже имеется в App, будем удалять соответствующие его фрагменты.

import React from "react"
import ContactCard from "./ContactCard" function App() { return ( <div className="contacts"> <ContactCard name="Mr. Whiskerson" imgUrl="http://placekitten.com/300/200" phone="(212) 555-1234" email="mr.whiskaz@catnap.meow" /> <ContactCard name="Fluffykins" imgUrl="http://placekitten.com/400/200" phone="(212) 555-2345" email="fluff@me.com" /> <ContactCard name="Destroyer" imgUrl="http://placekitten.com/400/300" phone="(212) 555-3456" email="ofworlds@yahoo.com" /> <ContactCard name="Felix" imgUrl="http://placekitten.com/200/100" phone="(212) 555-4567" email="thecat@hotmail.com" /> </div> )
} export default App

Правда, одной только передачи свойств компоненту недостаточно для того, чтобы они были бы в нём использованы. Страница, которая будет сформирована вышеприведённым компонентом App, будет содержать четыре одинаковых карточки, данные которых заданы в коде компонента ContactCard, который пока не знает о том, что ему делать с переданными ему свойствами.

Данные карточек жёстко заданы в коде, компонент не умеет работать с переданными ему свойствами

Поэтому сейчас пришло время поговорить о том, как компонент ContactCard может работать со свойствами, передаваемыми ему при создании его экземпляров.

При этом код компонента будет выглядеть так: Приступим к решению этой задачи, указав, при объявлении функции ContactCard, что она принимает параметр props.

import React from "react" function ContactCard(props) { return ( <div className="contact-card"> <img align="center" src="http://placekitten.com/300/200"/> <h3><font color="#3AC1EF">▍Mr. Whiskerson</font></h3> <p>Phone: (212) 555-1234</p> <p>Email: mr.whiskaz@catnap.meow</p> </div> )
} export default ContactCard

На самом деле, этот параметр можно назвать как угодно, но в React принято называть его именно props, и те свойства, о которых мы тут говорим, часто называют просто «props».

Свойствами этого объекта являются свойства, переданные компоненту при создании его экземпляра. Параметр props — это объект. Кроме того, у него будут свойства props.imgUrl, props.phone, props.email. То есть, например, в нашем объекте props будет свойство props.name, содержащее имя кошки, переданное компоненту при создании его экземпляра. Для того чтобы в этом убедиться, добавим в начало функции ContactCard команду console.log(props).

import React from "react" function ContactCard(props) { console.log(props) return ( <div className="contact-card"> <img align="center" src="http://placekitten.com/300/200"/> <h3><font color="#3AC1EF">▍Mr. Whiskerson</font></h3> <p>Phone: (212) 555-1234</p> <p>Email: mr.whiskaz@catnap.meow</p> </div> )
} export default ContactCard

Это позволит вывести объект props, получаемый компонентом, в консоль.

Объект props в консоли

Их именно столько из-за того, что мы создаём четыре экземпляра компонента ContactCard. Тут можно видеть вывод четырёх объектов из ContactCard.js.

Всё это даёт нам возможность использовать в коде компонента, вместо жёстко заданных значений, то, что передано ему при создании его экземпляра, доступное в виде свойств объекта props.

Что если мы попытаемся воспользоваться свойством props.imgUrl так:

<img align="center" src=props.imgUrl/>

На первый взгляд такая конструкция может сработать, но вспомним о том, что тут нам нужно использовать сущность из JavaScript в JSX-коде. О том, как это делается, мы говорили на одном из предыдущих занятий. А именно, в нашем случае свойство объекта нужно заключить в фигурные скобки:

<img align="center" src=/>

Переработаем по такому же принципу другие элементы, возвращаемые компонентом, после чего его код примет следующий вид:

import React from "react" function ContactCard(props) { return ( <div className="contact-card"> <img align="center" src={props.imgUrl}/> <h3><font color="#3AC1EF">▍{props.name}</font></h3> <p>Phone: {props.phone}</p> <p>Email: {props.email}</p> </div> )
} export default ContactCard

Обратите внимание на то, что в полях для вывода телефона и адреса электронной почты мы оставили тексты Phone: и Email: с пробелами, следующими за ними, так как эти тексты используются во всех компонентах. Если теперь взглянуть на страницу приложения, то можно заметить, что она содержит четыре разных карточки.

Страница, сформированная с использованием универсального компонента

Что если некоему компоненту нужно будет, например, передать 50 свойств? Наш компонент принимает всего четыре свойства. В таких случаях можно воспользоваться другим способом передачи свойств компонентам. Пожалуй, передавать каждое такое свойство отдельной строкой, как это сделано в компоненте App, будет неудобно. Вот как это может выглядеть на примере первого компонента: Он заключается в том, что, при создании экземпляра компонента, ему передаётся не список свойств, а объект со свойствами.

import React from "react"
import ContactCard from "./ContactCard" function App() { return ( <div className="contacts"> <ContactCard contact={{ name: "Mr. Whiskerson", imgUrl: "http://placekitten.com/300/200", phone: "(212) 555-1234", email: "mr.whiskaz@catnap.meow" }} /> <ContactCard name="Fluffykins" imgUrl="http://placekitten.com/400/200" phone="(212) 555-2345" email="fluff@me.com" /> <ContactCard name="Destroyer" imgUrl="http://placekitten.com/400/300" phone="(212) 555-3456" email="ofworlds@yahoo.com" /> <ContactCard name="Felix" imgUrl="http://placekitten.com/200/100" phone="(212) 555-4567" email="thecat@hotmail.com" /> </div> )
} export default App

Нельзя сказать, что этот подход значительно сократил количество кода, используемого для описания экземпляра компонента. Дело в том, что свойства, передаваемые компоненту, всё так же жёстко заданы в коде, хотя мы и передаём компоненту лишь один объект. Преимущества этого подхода можно будет ощутить в ситуациях, когда данные для компонента получают из неких внешних источников. Например — из JSON-файла.

Вот как теперь будет выглядеть его страница. В ходе модификации кода компонента App, используемого для создания первого экземпляра компонента ContactCard, правильная работа приложения была нарушена.

Нарушение правильной работы приложения

Для того чтобы в этом разобраться, полезно будет проанализировать происходящее с помощью команды console.log(props). Как это можно это исправить?

Анализ объекта props

Как видно, объект props первого компонента отличается от такого же объекта второго и следующих компонентов.

Здесь же первый компонент получает лишь одно свойство — contact. В компоненте ContactCard мы пользуемся объектом props исходя из предположения о том, что у него есть свойства name, imgUrl и прочие подобные. Это приводит к тому, что у объекта props оказывается лишь одно свойство — contact, являющееся объектом, а в коде компонента работа с подобной структурой не предусмотрена.

Для этого, например, для доступа к свойству name, достаточно воспользоваться конструкцией вида props.contact.name в коде компонента. Перевести наш компонент на модель использования лишь одного свойства-объекта contact, содержащего другие свойства, довольно просто. Аналогичные конструкции позволяют правильно работать с другими нужными нам свойствами.

Переработаем код компонента с учётом передачи ему единственного свойства-объекта contact, содержащего другие свойства:

import React from "react" function ContactCard(props) { console.log(props) return ( <div className="contact-card"> <img align="center" src={props.contact.imgUrl}/> <h3><font color="#3AC1EF">▍{props.contact.name}</font></h3> <p>Phone: {props.contact.phone}</p> <p>Email: {props.contact.email}</p> </div> )
} export default ContactCard

Первый компонент теперь должен будет выводиться нормально, но мы этого, на данном этапе работы над проектом, не увидим, так как система сообщит нам о множестве ошибок, связанных с тем, что несколько экземпляров компонента ContactCard, создаваемые в компоненте App, не получают свойство-объект contact. При выполнении кода это свойство будет иметь значение undefined. В результате производится попытка обратиться к некоему свойству значения undefined, что и приводит к возникновению ошибки. Исправим это, переработав код компонента App, ответственный за формирование компонентов ContactCard:

import React from "react"
import ContactCard from "./ContactCard" function App() { return ( <div className="contacts"> <ContactCard contact={{ name: "Mr. Whiskerson", imgUrl: "http://placekitten.com/300/200", phone: "(212) 555-1234", email: "mr.whiskaz@catnap.meow" }} /> <ContactCard contact={{ name: "Fluffykins", imgUrl: "http://placekitten.com/400/200", phone: "(212) 555-2345", email: "fluff@me.com" }} /> <ContactCard contact={{ name: "Destroyer", imgUrl: "http://placekitten.com/400/300", phone: "(212) 555-3456", email: "ofworlds@yahoo.com" }} /> <ContactCard contact={{ name: "Felix", imgUrl: "http://placekitten.com/200/100", phone: "(212) 555-4567", email: "thecat@hotmail.com" }} /> </div> )
} export default App

Теперь страница приложения будет выглядеть так же, как раньше.

Например — можете поработать с кодом, добавить новые свойства, передаваемые компоненту, и попытаться использовать их в компоненте. Как обычно, рекомендуется самостоятельно поэкспериментировать с изученными сегодня концепциями для того, чтобы лучше их усвоить.

Итоги

Сегодня мы познакомились с концепцией свойств, которые можно передавать компонентам React для того, чтобы управлять их поведением и внешним видом. Эти свойства напоминают атрибуты HTML-элементов, но, пользуясь свойствами в компонентах, программист самостоятельно принимает решение о том, какой смысл они несут и что именно делать с ними в компоненте. В следующий раз вас будет ждать практическое занятие по работе со свойствами компонентов и по стилизации.

Уважаемые читатели! Как вы экспериментировали с кодом сегодняшнего примера для того, чтобы лучше разобраться со свойствами React-компонентов?


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Что сообщество потребителей делает для уничтожения СПО

Даже Microsoft очень неплохо приложило руку к тому, чтобы популяризировать СПО в массах. На волне внедрения DevOps да и в целом развития ИТ в массах всё больше и больше становится популярным использование СПО (Свободное Программное Обеспечение). Одна проблема — пользователи ...

Cataclysm Dark Days Ahead, статический анализ и рогалики

Скорее всего, из названия статьи вы уже догадались, что в центре внимания ошибки в исходном коде. Но это вовсе не единственное, о чем пойдет речь в этой статье. Если кроме С++ и ошибок в чужом коде вас привлекают необычные игры ...