Кампанэнты (Components) і ўласьцівасьці (Props)

Кампанэнты дазваляюць падзяліць UI на незалежныя, годныя да паўторнага выкарыстаньня часткі, і думаць аб кожнай частцы паасобку. Гэтая старонка ўяўляе сабой уводзіны ў ідэю кампанэнтаў. Вы можаце знайсьці падрабязную спасылку на API кампанэнта тут.

Канцэптуальна, кампанэнты падобныя да функцый JavaScript. Яны прымаюць зьменлівыя даныя на ўваходзе (так званыя “props”) і вяртаюць React-элемэнты, якія апісваюць тое, што павінна зьявіцца на экране.

Функцыянальныя (Functional) і клясавыя (Class) кампанэнты

Самы просты спосаб азначыць кампанэнт — напісаць функцыю JavaScript:

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

Гэтая функцыя зьяўляецца сапраўдным React-кампанэнтам, таму што яна прымае адзіны аргумэнт аб’екта “props” (што азначае ўласьцівасьці) з данымі ды вяртае 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 гэтаму кампанэнту як адзіны аб’ект. Гэты аб’ект мы называем “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 name="Sara" />.
  2. React выклікае кампанэнт Welcome з дапамогай {name: 'Sara'} у якасьці props.
  3. Наш кампанэнт Welcome вяртае элемэнт <h1>Hello, Sara</h1> у якасьці выніку.
  4. React DOM эфэктыўна абнаўляе DOM, каб адпавядаў <h1>Hello, Sara</h1>.

Заўвага: Заўсёды пачынайце імёны кампанэнтаў зь вялікае літары.

React трактуе кампанэнты, якія пачынаюцца з маленькіх літар, як тэгі DOM. Напрыклад, <div /> уяўляе сабой тэг HTML div, а <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, і паступова падыходзіць да вяршыні герархіі прадстаўленьня.

Выманьне кампанэнтаў

Ня бойцеся разьбіваць кампанэнты на меншыя кампанэнты.

Напрыклад, разгледзім наступны кампанэнт 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. Таму мы далі яму ў якасьці 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.

Выманьне кампанэнтаў можа спачатку падацца цяжкай працай, але гэта акупляецца ў вялікіх прыкладных праграмах наборам кампанэнтаў, якія можна паўторна выкарыстоўваць. Добрае эмпірычнае правіла: калі частка вашага UI выкарыстоўваецца некалькі разоў (Button, Panel, Avatar), альбо складаная сама па сабе (App, FeedStory, Comment), то яна зьяўляецца добрым кандыдатам быць кампанэнтам, які можна паўторна выкарыстоўваць.

Props прызначаныя толькі для чытаньня

Калі вы аб’яўляеце кампанэнт як фунцыю або кляс, то ён ніколі ня можа зьмяняць свае ўласныя props. Разгледзім наступную функцыю sum:

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

Такія функцыі называюцца “чыстымі”, таму што яны не спрабуюць зьмяніць свае ўваходныя даныя і заўсёды вяртаюць адзін і той жа вынік для адных і тых жа ўваходных даных.

У супрацьлегласьць, гэтая функцыя нячыстая, таму што яна зьмяняе свае ўласныя ўваходныя даныя:

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

React даволі гнуткі, але ён мае адно строгае правіла:

Усе React-кампанэнты павінны дзейнічаць як чыстыя функцыі ў дачыненьні да сваіх props.

Вядома, што карыстальніцкія інтэрфэйсы (UIs) прыкладных праграм зьяўляюцца дынамічнымі й зьмяняюцца зь цягам часу. У наступным разьдзеле, мы прадставім новае паняцьце стану “state”. State дазваляе React-кампанэнтам зьмяняць зь цягам часу іхныя выходныя даныя ў адказ на дзеяньні карыстальніка, сеткавыя сыгналы, ці што-небудзь яшчэ, не парушаючы гэтага правіла.