Компоненты и свойства в React
Компоненты позволяют разделить интерфейс на независимые, повторно используемые части и думать о каждой части по отдельности.
В принципе, компоненты - это все равно, что функции в JavaScript. Они принимают произвольные свойства (так называемые "props") и возвращают React-элементы, описывающие то, что должно появиться на экране.
Функциональные и классовые компоненты
Самый простой способ определить компонент - это написать функцию на JavaScript:
function Welcome(props) {
return <h1>Привет, {props.name}</h1>;
}
Эта функция is a valid React component because it accepts a single "props" object argument with data и выдает элемент React. Мы называем такие компоненты «функциональными», поскольку они по сути JavaScript-функции.
Вы также можете использовать ES6 класс чтобы определить компонент:
class Welcome extends React.Component {
render() {
return <h1>Привет, {this.props.name}</h1>;
}
}
Два вышеуказанных компонента, с точки зрения React, эквивалентны.
Классы имеют некоторые дополнительные функции, которые мы рассмотрим в разделе Состояние и жизненный цикл. Until then, we will use functional components for their conciseness.
Отрисовка компонента
Раньше мы встречались только с React-элементами, которые представляли собой DOM теги:
const element = <div />;
Тем не менее, элементы могут также представлять компоненты, определенные пользователем:
const element = <Welcome name="Sara" />;
Когда React видит элемент, представляющий собой определенный пользователем компонент, он передает JSX атрибуты данного компонента в качестве отдельного объекта. Мы называем этот объект "props".
Например, этот код отображает на странице фразу "Привет, Сара":
function Welcome(props) {
return <h1>Привет, {props.name}</h1>;
}
const element = <Welcome name="Сара" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Смотреть пример на CodePen.
Давайте разберем, что происходит в этом примере:
- Мы вызываем
ReactDOM.render()
с элементом <Welcome name="Sara" />
.
- React вызывает компонент Welcome со свойством
{name: 'Сара'}
.
- Наш компонент Welcome возвращает
<h1>Привет, Сара</h1>
.
- React DOM обновляет DOM, чтобы он соответствовал
<h1>Привет, Сара</h1>
.
Всегда пишите имена компонентов с заглавной буквы. Напрмер, <div />
представляет собой DOM-тег, а <Welcome />
- компонент и требует наличия Welcome
в области действия.
Composing Components
Компоненты могут ссылаться на другие компоненты в результате их выполнения. Это позволяет использовать один и тот же компонент абстракции для любого уровня детализации. Кнопка, форма, диалог, экран: в React приложениях все они обычно выражаются в качестве компонентов.
Например, мы можем создать компонент App
, который много раз отобразит компонент Welcome
:
function Welcome(props) {
return <h1>Привет, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Сара" />
<Welcome name="Жанна" />
<Welcome name="Марианна" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Смотреть пример на CodePen.
Как правило, новые React приложения имеют один компонент App
на самом верху. Тем не менее, если вы интегрируете React в существующее приложение, вы можете начать снизу вверх с небольшого компонента, например, Button
и постепенно двигаться вверх.
Компоненты должны возвращать один корневой элемент. Именно поэтому мы добавили <div>
чтобы внедрить все элементы <Welcome />
.
Не бойтесь разделять компоненты на более мелкие. Взгляните, к примеру, на компонент 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
(дата) через props и описывает комментарий на странице социальной сети. Этот компонент будет сложно изменить из-за всех вложений и будет затруднительно повторно использовать отдельные части. Давайте извлечем несколько компонентов из него. Начнем с Avatar
:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
Avatar
не нужно знать, что он отображается внутри Comment
. Именно поэтому мы дали его prop более общее название: 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>
);
}
Смотреть пример на CodePen.
Вначале, извлечение компонентов может показаться изнурительной работой, но солидный набор повторно используемых компонентов полностью окупается в больших приложениях. Запомните правило: если часть вашего пользовательского интерфейса используется несколько раз (например: Button
, Panel
, Avatar
), или является сложной сама по себе (например: App
, FeedStory
, Comment
), то она является хорошим кандидатом для многократно используемого компонента.
Свойства только-для-чтения
Если вы объявляете компонент как функцию или класса, он никогда не должен изменять свои собственные свойства. Взгляните на функцию sum
:
function sum(a, b) {
return a + b;
}
Такие функции называются "чистыми", потому что они не пытаются ничего изменить и всегда возвращают один и тот же результат для одних и тех агументов функции. А вот эта функция является "нечистой", потому что она изменяет свои собственные входные данные:
function withdraw(account, amount) {
account.total -= amount;
}
React довольно гибкий, но он имеет одно строгое правило: все React компоненты должны действовать как чистые функции по отношению к своим свойствам. Конечно, UI являются динамическими и меняются с течением времени. В следующем разделе Состояние и жизненный цикл мы введем новое понятие "state" (состояние). Состояние позволяет компонентам React изменять выдаваемый ими результат с течением времени в ответ на действия пользователя, ответы сервера, и пр. не нарушая этого правила.