Компоненты и пропсы
These docs are old and won’t be updated. Go to react.dev for the new React docs.
These new documentation pages teach modern React and include live examples:
Компоненты позволяют разбить интерфейс на независимые части, про которые легко думать в отдельности. Их можно складывать вместе и использовать несколько раз. На этой странице мы ознакомимся с самой идеей компонентов — детальное описание API находится здесь.
Во многом компоненты ведут себя как обычные функции JavaScript. Они принимают произвольные входные данные (так называемые «пропсы») и возвращают React-элементы, описывающие, что мы хотим увидеть на экране.
Функциональные и классовые компоненты
Проще всего объявить React-компонент как функцию:
function Welcome(props) {
return <h1>Привет, {props.name}</h1>;
}Эта функция — компонент, потому что она получает данные в одном объекте («пропсы») в качестве параметра и возвращает React-элемент. Мы будем называть такие компоненты «функциональными», так как они буквально являются функциями.
Ещё компоненты можно определять как классы ES6:
class Welcome extends React.Component {
render() {
return <h1>Привет, {this.props.name}</h1>;
}
}С точки зрения React, эти два компонента эквивалентны.
Функциональным и классовым компонентам доступны дополнительные возможности, о которых мы поговорим в следующих главах.
Как отрендерить компонент
Пока что мы только встречали React-элементы, представляющие собой DOM-теги:
const element = <div />;Но элементы могут описывать и наши собственные компоненты:
const element = <Welcome name="Алиса" />;Когда React встречает подобный элемент, он собирает все JSX-атрибуты и дочерние элементы в один объект и передаёт их нашему компоненту. Этот объект называется «пропсы» (props).
Например, этот компонент выведет «Привет, Алиса» на страницу:
function Welcome(props) { return <h1>Привет, {props.name}</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Алиса" />;root.render(element);Давайте разберём, что именно здесь происходит:
- Мы вызываем
root.render()c React-элементом<Welcome name="Алиса" />. - React вызывает наш компонент
Welcomeс пропсами{name: 'Алиса'}. - Наш компонент
Welcomeвозвращает элемент<h1>Привет, Алиса</h1>в качестве результата. - React DOM делает минимальные изменения в DOM, чтобы получилось
<h1>Привет, Алиса</h1>.
Примечание: Всегда называйте компоненты с заглавной буквы.
Если компонент начинается с маленькой буквы, React принимает его за DOM-тег. Например,
<div />это div-тег из HTML, а<Welcome />это уже наш компонентWelcome, который должен быть в области видимости.Чтобы узнать больше про это соглашение, прочитайте Углублённое изучение JSX.
Композиция компонентов
Компоненты могут ссылаться на другие компоненты в возвращённом ими дереве. Это позволяет нам использовать одну и ту же абстракцию — компоненты — на любом уровне нашего приложения. Неважно, пишем ли мы кнопку, форму или целый экран: все они, как правило, представляют собой компоненты в React-приложениях.
Например, компонент App может отрендерить компонент Welcome несколько раз:
function Welcome(props) {
return <h1>Привет, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Алиса" /> <Welcome name="Базилио" /> <Welcome name="Буратино" /> </div>
);
}В приложениях, написанных на 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. Поэтому мы дали его пропу чуть менее конкретное имя — user, а не author.
Пропсы следует называть так, чтобы они имели смысл в первую очередь с точки зрения самого компонента, а уже во вторую тех компонентов, которые его рендерят.
Теперь можно немножко упростить наш 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>
);
}Извлечение компонентов может сначала показаться неблагодарной работой. Тем не менее, в больших приложениях очень полезно иметь палитру компонентов, которые можно многократно использовать. Если вы не уверены, извлекать компонент или нет, вот простое правило. Если какая-то часть интерфейса многократно в нём повторяется (Button, Panel, Avatar) или сама по себе достаточно сложная (App, FeedStory, Comment), имеет смысл её вынести в независимый компонент.
Пропсы можно только читать
Компонент никогда не должен что-то записывать в свои пропсы — вне зависимости от того, функциональный он или классовый.
Возьмём для примера функцию sum:
function sum(a, b) {
return a + b;
}Такие функции называют «чистыми», потому что они не меняют свои входные данные и предсказуемо возвращают один и тот же результат для одинаковых аргументов.
А вот пример нечистой функции — она записывает данные в свои же аргументы:
function withdraw(account, amount) {
account.total -= amount;
}React достаточно гибкий, но есть одно правило, которое нельзя нарушать:
React-компоненты обязаны вести себя как чистые функции по отношению к своим пропсам.
Конечно, интерфейсы приложений обычно изменяются с течением времени. В следующей главе мы узнаем о том, что такое «состояние» компонента. Состояние даёт компонентам возможность реагировать на действия пользователя, ответы сервера и другие события, не нарушая чистоту компонента.