Компоненты React и свойства (Components and Props)

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

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

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

Простейший вариант определения компонента, запись его в виде JavaScript-функции:

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

Эта функция является допустимым React компонентом, поскольку она принимает как аргумент одно «свойство» объекта  и возвращает React элемент. Мы называем такие компоненты «функциональными», потому что они в буквальном смысле функция JavaScript.

Также, для описания  компонента можно использовать  классы ES6 :

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Два вышеуказанных компонента эквивалентны с точки зрения React.

Классы имеют некоторые дополнительные возможности, которые мы рассмотрим в следующих разделах. До тех пор, мы будем использовать функциональные компоненты, поскольку они более лаконичны.

 

Отрисовка компонентов 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')
);

Попробовать на CodePen.

Давайте подведем итог, что мы узнали в этом примере:

  1. Мы вызываем ReactDOM.render() с элементом <Welcome имя = «Сара» /> .
  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 в существующее приложение, вы можете начать создавать иерархию компонентов снизу вверх, стартуя с небольшого компонента типа кнопки и постепенно подниматься к вершине иерархии представлений.

Предостережение:

Компоненты должны возвращать один корневой элемент. Именно поэтому мы добавили в <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 (как строку) и 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 c следующим именем пользователя:

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), или разработанный самостоятельно UI (App, FeedStory, Comment).

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

Если вы объявляете компонент в виде функции или класса, он никогда не должен изменять свои собственные реквизита. Рассмотрим эту функцию sum :

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

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

В отличие от следующей функции, которая меняет введённый аргумент:

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

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

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

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