Компоненты и свойства


Компоненты позволяют разделять интерфейс на самостоятельные повторно применяемые части и анализировать каждую из них в отдельности. По идее, компоненты это функции JavaScript. Они принимают произвольное свойство и отдают обратно React элементы, описывающие то, что должно отображаться на экране.

Функциональные и классовые компоненты


Самый простой способ определить компонент это написать JavaScript функцию:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}


Эта функция является активным компонентом React потому что она принимает на вход объект argument c данными и выдает элемент 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 атрибуты этому компоненту как единому объекту.

Например, код отображает на странице «Привет, Сара»:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);


Попробуйте повторить этот пример в CodePen.

Давайте рассмотрим, что происходит в данном примере:

  1. Мы создаем свойство ReactDOM.render() с элементом 

    <Welcome name="Sara" />

  2. React сигнализирует компоненту Welcome со свойством 

    {name: 'Sara'}

  3. Наш компонент Welcome выдает 

    <h1>Hello, Sara</h1>

    как результат;

  4. 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')
);


Попробуйте повторить этот пример в 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>
  );
}


Попробуйте повторить этот пример в CodePen.

Он принимает объект author, линию text и данные как входной сигнал и отображает комментарий на странице социальной сети.

Этим компонентом может быть сложно управлять из за множественных вложений, а также некоторые его части могут использоваться повторно. Давайте выделим из него некоторые компоненты.

Сначала выделим 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>
  );
}


Попробуйте повторить этот пример в CodePen.

Поначалу, извлечение компонентов может показаться трудоемкой задачей, но большое разнообразие компонентов повторного использования позволяет создавать более крупные приложения. Отличное правило: если часть вашего UI используется несколько раз (Button, Panel, Avatar), или она является сложной сама по себе (App, FeedStory,Comment), то она может послужить отличным компонентом повторного использования.

Свойства только для чтения


Если вы рассматриваете компонент в виде функции или класса, то он никогда не должен менять свои свойства. 

Обратите внимание на функцию sum:

function sum(a, b) {
  return a + b;
}


Такие функции называются чистыми, поскольку они не пытаются ничего изменить и всегда отдают тот же результат.

А эта функция не является чистой, потому что она делает изменения:

function withdraw(account, amount) {
  account.total -= amount;
}


React является довольно гибким инструментом, но у него есть одно строгое правило:

Все компоненты React должны действовать как чистые функции, сохраняя свои свойства.

Безусловно, UI приложений динамичны и со временем они подвергаются изменениям. В следующем разделе мы изучим новый концепт «состояния». Состояние позволяет компонентам React изменять свой результат выполнения в течении времени в ответ на действия пользователей, запросы сервера и т.д., не нарушая главного правила.