Компоненти и Props

Компонентите ви дават възможност да разделите потребителския ви интерфейс на независими, многократно използваеми части и да мислите за всяка от тях в изолация. Тази страница предлага въведение към идеята за компоненти. Тук можете да намерите подробна справка за API на компонентите.

Концептуално, компонентите са като JavaScript функции. Те приемат произволни обекти (наричани “props”) и връщат React елементи, описващи какво трябва да се появи на екрана.

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

Най-простия начин за дефиниране на компонент е да се напише JavaScript функция:

function Welcome(props) {
  return <h1>Здравейте, {props.name}</h1>;
}

Тази функция е валиден React компонент, защото приема един обект “props” (идва от “properties”) с данни и връща React елемент. Ние наричаме такива компоненти “функция компоненти”, защото те са буквално JavaScript функции.

Може също да използвате ES6 класове за да дефинирате компонент:

class Welcome extends React.Component {
  render() {
    return <h1>Здравейте, {this.props.name}</h1>;
  }
}

Горните два компонента са еквивалентни от гледна точка на React.

Класовете имат някои допълнителни функции, които ще обсъдим в следващите раздели. Дотогава ще използваме функция компоненти заради тяхната компактност.

Rendering a Component

По-рано срещнахме само React елементи, които представляват DOM тагове:

const element = <div />;

Освен това, елементите могат също да представляват дефинирани от потребителя компоненти:

const element = <Welcome name="Иван" />;

Когато React срещне елемент, представляващ дефиниран от потребителя компонент, той предава JSX атрибутите и децата на този компонент като един обект. Ние наричаме този обект “props”.

Този код например показва „Здравейте, Иван“ на страницата:

function Welcome(props) {
  return <h1>Здравейте, {props.name}</h1>;
}

const element = <Welcome name="Иван" />;

ReactDOM.render(
  element,
  document.getElementById('root')
);

Опитайте примера в CodePen

Нека повторим какво се случва в този пример:

  1. Използваме ReactDOM.render() с елемента <Welcome name="Иван" />.
  2. React използва Welcome компонента с {name: 'Иван'} като props.
  3. Нашия Welcome компонент връща <h1>Здравейте, Иван</h1> елемент като резултат.
  4. React DOM ефективно актуализира DOM, който съответства на <h1>Здравейте, Иван</h1>.

Забележа: Винаги започвайте имената на компонентите с главна буква.

React третира компонентите, започващи с малки букви като DOM тагове. Например, <div /> представлява HTML div таг, но <Welcome /> представлява компонент и изисква функцията Welcome да бъде в обхват.

За да научите повече за идеите зад тази конвенция, моля прочетете JSX в детайли.

Композиране на компоненти

Всеки компонент може да използва други компоненти като резултат. Това ни позволява да използваме същата абстракция на компонента на всяко ниво. Бутон, Форма, dialog: в React приложения, всички те обикновено се изразяват като компоненти.

Например, можем да създадем App компонент, който използва Welcome многократно:

function Welcome(props) {
  return <h1>Hello, {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 и постепенно да стигнете до върха на йерархията.

Extracting Components

Не се страхувайте да разделяте компонентите на по-малки компоненти.

Например разгледайте този 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 (дата) като props и описва коментар в уебсайт на социална медия.

Този компонент може да се окаже труден за промяна поради многото вложени елементи. Също така е трудно да преизползваме отделните му части. Нека го разделим на няколко по-малки компонента.

Първо, ще извлечем Avatar:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

Avatar не е нужно да знае, че той се визуализира от Comment. Ето защо сме дали по-общо име: 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), да го приемем като добър кандидат за компонент за многократна употреба.

Props са Read-Only

Дали декларирате компонент като функция или клас, той никога не трябва да променя собствените си props. Вземете в предвид тази sum функция:

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

Такива функции се наричат “pure”, защото те не се опитват да променят входните си данни и винаги връщат същия резултат за същите входни данни.

За разлика от това, тази функция е impure, защото променя собствените си входни данни:

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

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

Всички React компоненти трябва да действат като pure функции по отношение на техните props.

Разбира се, потребителските интерфейси на приложенията са динамични и се променят с времето. В следващата секция, ще въведем нова концепция за “state”. State дава възможност на React компоненти да променят тяхния резултат с течение на времето в отговор на потребителски действия, отговори от външни заявки и други, без да нарушаваме това правило.