2.5 Компоненты и свойства
Компоненты позволяют разделить UI на независимые, повторно используемые части и работать с каждой из них отдельно.
Концептуально, компоненты похожи на JavaScript-функции. Они принимают произвольные данные (называемые props) и возвращают React-элементы, которые описывают то, что должно появиться на экране.
Самый простой способ объявить компонент – это написать JavaScript-функцию:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
Эта функция является корректным React-компонентом, потому что она принимает
единственный объект props
с данными в качестве аргумента и возвращает React-элемент.
Такие компоненты называются «функциональными», так как они и есть JavaScript-функции.
Компонент можно объявить другим способом. Для этого нужно использовать ES6-класс:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Два приведенных выше компонента являются эквивалентными с точки зрения React. Но пока мы будем использовать функциональные компоненты, так как они короче.
Ранее, мы наталкивались лишь на React-элементы, которые представляли собой DOM-теги:
const element = <div/>;
Тем не менее, элементы могут быть представлены пользовательскими(кастомными) компонентами:
const element = <Welcome name="Sara" />;
Когда React видит, что элемент представляет собой пользовательский компонент, он
передает все JSX-атрибуты в этот компонент единым объектом. Такой
объект называется props
.
Например, этот код отрисовывает на странице «Hello, Sara»:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Давайте прорезюмируем то, что произошло в этом примере:
- Мы вызвали
ReactDOM.render()
с элементом<Welcome name="Sara" />
. - React вызывает компонент
Welcome
с объектом{name: 'Sara'}
в качестве свойствprops
. - Наш компонент
Welcome
возвращает элемент<h1>Hello, Sara</h1>
как результат. - React DOM эффективно обновляет DOM, чтобы соответствовать
<h1>Hello, Sara</h1>
Внимание!
Всегда именуйте свои компоненты с большой буквы.Например,
<div/>
представляет собой DOM-тег, а <Welcome/>
представляет собой компонент и
требует, чтобы Welcome
был в области видимости.
Компоненты могут ссылаться на другие компоненты в своём выводе (результате отрисовки). Это позволяет нам использовать ту же самую абстракцию компонента для любого уровня детализации. Кнопка, форма, диалог, экран: в React-приложении все эти сущности выражены компонентами.
К примеру, мы можем создать компонент App
, который
отрисовывает компонент Welcome
много раз:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Как правило, новые React-приложения имеют единственный компонент App
на самом верху иерархии.
Тем не менее, если вы интегрируете React в уже существующее приложение, вы можете начать снизу
вверх с маленького компонента, такого как Button
и постепенно двигаться вверх по
иерархии отображения.
Не бойтесь разделять компоненты на более мелкие компоненты.
Рассмотрим пример с компонентом Comment
:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Он принимает author
(объект), text
(строка) и date
(дата) как
свойства, и описывает комментарий на социальном веб-сайте.
Данный компонент довольно сложно изменить из-за его вложенности. Также тяжело повторно использовать и его составные части. Давайте извлечем из него несколько небольших компонентов, упростив исходный компонент.
Для начала давайте извлечем из него компонент Avatar
:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}/>
);
}
Компонент Avatar
не знает о том, что он находится внутри
компонента Comment
. Вот почему мы дали свойству его объекта props
более
общее имя: user
, вместо author
.
Совет!
Мы рекомендуем именоватьprops
с точки зрения компонента, а не контекста,
в котором он будет использован.
Сейчас мы можем немного упростить компонент Comment
:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Далее, мы извлечем компонент UserInfo
, который отрисовывает компонент Avatar
рядом с именем пользователя:
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
Это позволяет нам еще больше упростить компонент Comment
:
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Извлечение компонентов по началу может показаться рутинной работой. Однако набор универсальных, переиспользуемых, отлаженных компонентов с лихвой окупит все усилия в больших приложениях, экономя массу времени.
Золотое правило!
Если какая-то часть вашего UI используется неоднократно (Button
, Panel
, Avatar
), или
довольно сложная (составная) (App
, FeedStory
, Comment
) – она
хороший кандидат на то, чтобы стать переиспользуемым компонентом.
Компонент, объявленный как функция или класс, никогда не должен
модифицировать свои свойства props
. Рассмотрим
эту sum
функцию:
function sum(a, b) {
return a + b;
}
Такие функции называются «чистыми». Потому что они не изменяют свои аргументы и всегда возвращают одинаковый результат для одних и тех же аргументов.
В противоположность им, следующая функция не является чистой, потому что она изменяет свои аргументы:
function withdraw(account, amount) {
account.total -= amount;
}
React является очень гибким, но он имеет одно строгое правило:
Все React-компоненты должны работать как чистые функции в отношении своих свойств
props
.
Конечно, UI приложения – динамический и изменяется со временем. В следующем разделе мы познакомимся с новой концепцией «состояния». Состояние позволяет React-компонентам изменять их вывод со временем в ответ на действия пользователя, ответы сети или что-то другое, не нарушая данное правило.