Передача пропсів до компонента

Компоненти React використовують пропси для взаємодії між собою. Кожен батьківський компонент може передати деяку інформацію своїм дочірнім компонентам, передаючи їм пропси. Пропси можуть нагадувати вам HTML-атрибути, але ви можете передавати через них будь-яке значення JavaScript, включаючи об’єкти, масиви та функції.

You will learn

  • Як передавати пропси до компонента
  • Як читати пропси з компонента
  • Як встановлювати значення за замовчуванням для пропсів
  • Як передавати JSX до компонента
  • Як пропси змінюються з часом

Знайомі пропси

Пропси — це інформація, яку ви передаєте до тегу JSX. Наприклад, className, src, alt, width та height — деякі з пропсів, які ви можете передати до тегу <img>:

function Avatar() {
  return (
    <img
      className="avatar"
      src="https://i.imgur.com/1bX5QH6.jpg"
      alt="Лін Ланьїн (Lin Lanying)"
      width={100}
      height={100}
    />
  );
}

export default function Profile() {
  return (
    <Avatar />
  );
}

Пропси, які ви можете передати до тегу <img>, визначені заздалегідь (ReactDOM відповідає стандарту HTML). Але ви можете передати будь-які пропси до власних компонентів, таких як <Avatar>, щоб налаштувати їх. Ось як це зробити!

Передача пропсів до компонента

У цьому коді компонент Profile не передає жодних пропсів до свого дочірнього компонента Avatar:

export default function Profile() {
return (
<Avatar />
);
}

Ви можете передати деякі пропси до компонента Avatar у два кроки.

Крок 1: Передайте пропси до дочірнього компонента

Спочатку передайте деякі пропси до Avatar. Наприклад, давайте передамо два пропси: person (об’єкт) і size (число):

export default function Profile() {
return (
<Avatar
person={{ name: 'Лін Ланьїн (Lin Lanying)', imageId: '1bX5QH6' }}
size={100}
/>
);
}

Note

Якщо подвійні фігурні дужки після person= вас збивають з пантелику, згадайте, що вони просто об’єкт всередині фігурних дужок JSX.

Тепер ви можете читати ці пропси всередині компонента Avatar.

Крок 2: Читайте пропси всередині дочірнього компонента

Ви можете прочитати ці пропси, перераховуючи їх назви person, size, розділені комами всередині ({ та }) безпосередньо після function Avatar. Це дозволяє використовувати їх всередині коду Avatar як змінні.

function Avatar({ person, size }) {
// person та size доступні тут
}

Додайте трохи логіки до Avatar, яка використовує пропси person та size для рендеру, і готово.

Тепер ви можете налаштувати Avatar для рендеру багатьма різними способами з різними пропсами. Спробуйте змінити значення!

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar
        size={100}
        person={{ 
          name: 'Кацуко Сарухаші (Katsuko Saruhashi)', 
          imageId: 'YfeOqp2'
        }}
      />
      <Avatar
        size={80}
        person={{
          name: 'Аклілу Лемма (Aklilu Lemma)', 
          imageId: 'OKS67lh'
        }}
      />
      <Avatar
        size={50}
        person={{ 
          name: 'Лін Ланьїн (Lin Lanying)',
          imageId: '1bX5QH6'
        }}
      />
    </div>
  );
}

Пропси дозволяють вам думати про батьківські та дочірні компоненти незалежно. Наприклад, ви можете змінити проп person або size всередині Profile, не думаючи про те, як Avatar використовує їх. Аналогічно, ви можете змінити якAvatar використовує ці пропси, не звертаючи уваги на Profile.

Уявіть пропси як «ручки регулювання», які ви можете налаштовувати. Вони виконують ту саму роль, що і аргументи для функцій — насправді, пропси є єдиним аргументом вашого компонента! Функції компонентів React приймають один аргумент — об’єкт props:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

Зазвичай вам не потрібен весь об’єкт props сам по собі, тому ви розкладаєте його на окремі пропси.

Pitfall

**Не пропустіть пару фігурних дужок { та } всередині ( та ) при оголошенні пропсів:

function Avatar({ person, size }) {
// ...
}

Цей синтаксис називається “деструктуризація” і еквівалентний читанню пропcів з параметра функції:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

Вказання значення за замовчуванням для пропа

Якщо ви хочете задати пропу значення за замовчуванням, на яке він посилатиметься, якщо не вказано іншого, ви можете зробити це завдяки деструктуризації, поставивши = та значення за замовчуванням відразу після параметра:

function Avatar({ person, size = 100 }) {
// ...
}

Тепер, якщо <Avatar person={...} /> рендериться без пропу size, size буде встановлено на 100.

Значення за замовчуванням використовується тільки у випадку, якщо проп size відсутній або якщо ви передаєте size={undefined}. Але якщо ви передаєте size={null} або size={0}, значення за замовчуванням не буде використовуватись.

Передача пропсів за допомогою spread синтаксису JSX

Іноді передача пропсів стає дуже повторюваною:

function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}

Немає нічого поганого в повторюваному коді — він може бути більш зрозумілим. Але іноді ви можете цінувати лаконічність. Деякі компоненти передають всі свої пропси своїм дочірнім компонентам, як це робить Profile з Avatar. Оскільки вони не використовують жоден зі своїх пропсів напряму, розумніше буде вжити лаконічний синтаксис “spread”:

function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}

Це передає всі пропси Profile до Avatar без перерахування кожної назви окремо.

Використовуйте синтаксис spread з обережністю. Якщо ви використовуєте його в кожному іншому компоненті, то щось пішло не так. Часто це свідчить про те, що вам слід розбити ваші компоненти та передати дочірні елементи як JSX. Детальніше про це далі!

Передача JSX як children

Вкладення вбудованих тегів браузера є звичайною справою:

<div>
<img />
</div>

Іноді ви захочете вкладати свої власні компоненти так само:

<Card>
<Avatar />
</Card>

Коли ви вкладаєте вміст всередині тегу JSX, батьківський компонент отримує цей вміст у пропі з назвою children. Наприклад, компонент Card нижче отримає проп children зі значенням <Avatar /> і відображатиме його в div-обгортці:

import Avatar from './Avatar.js';

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Кацуко Сарухаші (Katsuko Saruhashi)',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}

Спробуйте замінити <Avatar> всередині <Card> на деякий текст, щоб побачити, як компонент Card може обгорнути будь-який вкладений вміст. Він не потребує “знати”, що саме рендериться всередині нього. Ви ще побачите цей гнучкий паттерн у багатьох місцях.

Ви можете уявляти компонент з пропом children як компонент з “отвором”, який може бути “заповнений” батьківськими компонентами довільним JSX. Ви часто будете використовувати проп children для візуальних обгорток: панелей, сіток і т.д.

Подібна до головоломки плитка Card зі слотами для "дочірніх" шматочків, наприклад текст або Avatar

Illustrated by Rachel Lee Nabors

Як пропси змінюються з часом

Компонент Clock, наведений нижче, отримує два пропси від батьківського компонента: color та time. (Код батьківського компонента не вказаний, оскільки він використовує стан, до деталей якого ми поки не будемо вдаватись.)

Спробуйте змінити колір у полі вибору нижче:

export default function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}

Цей приклад показує, що компонент може отримувати різні пропси з часом. Пропси не завжди є статичними! Тут проп time змінюється кожну секунду, а проп color змінюється, коли ви вибираєте інший колір. Пропси відображають дані компонента в будь-який момент часу, а не лише на початку.

Однак, пропси є незмінними (immutable). Коли компоненту потрібно змінити свої пропси (наприклад, відповідно до взаємодії користувача або нових даних), він повинен «попросити» свій батьківський компонент передати йому інші пропси — новий об’єкт! Його старі пропси будуть відкинуті, і, врешті-решт, рушій JavaScript звільнить пам’ять, яку вони займали.

Не намагайтеся «змінювати пропси». Коли вам потрібно відреагувати на вхідні дані користувача (наприклад, змінювати вибраний колір), вам потрібно «встановити стан», про який ви можете дізнатися у розділі Стан: Пам’ять компонента.

Recap

  • Щоб передати пропси, додайте їх до JSX, подібно до атрибутів HTML.
  • Щоб прочитати пропси, використовуйте синтаксис деструктуризації function Avatar({ person, size }).
  • Ви можете вказати значення за замовчуванням, наприклад, size = 100, яке використовується для відсутніх та undefined пропсів.
  • Ви можете передати всі пропси JSX за допомогою spread синтаксису <Avatar {...props} />, але не зловживайте цим!
  • Вкладений JSX, наприклад <Card><Avatar /></Card>, буде виглядати як проп children компонента Card.
  • Пропси в момент часу можуть бути використані тільки для читання: кожен рендер отримує нову версію пропсів.
  • Ви не можете змінювати пропси. Для інтерактивності використовуйте стан.

Challenge 1 of 3:
Винесіть компонент

Цей компонент Gallery містить дуже схожу розмітку для двох профілів. Винесіть компонент Profile з нього, щоб зменшити дублювання. Вам потрібно буде вибрати, які пропси передати йому.

import { getImageUrl } from './utils.js';

export default function Gallery() {
  return (
    <div>
      <h1>Визначні вчені</h1>
      <section className="profile">
        <h2>Марія Склодовська-Кюрі (Maria Skłodowska-Curie)</h2>
        <img
          className="avatar"
          src={getImageUrl('szV5sdG')}
          alt="Марія Склодовська-Кюрі (Maria Skłodowska-Curie)"
          width={70}
          height={70}
        />
        <ul>
          <li>
            <b>Професія: </b> 
            фізик та хімік
          </li>
          <li>
            <b>Нагороди: 4 </b> 
            (Нобелівська премія з фізики, Нобелівська премія з хімії, Медаль Дейві, Медаль Маттеуччі)
          </li>
          <li>
            <b>Відкрито: </b>
            полоній (хімічний елемент)
          </li>
        </ul>
      </section>
      <section className="profile">
        <h2>Кацуко Сарухаші (Katsuko Saruhashi)</h2>
        <img
          className="avatar"
          src={getImageUrl('YfeOqp2')}
          alt="Кацуко Сарухаші (Katsuko Saruhashi)"
          width={70}
          height={70}
        />
        <ul>
          <li>
            <b>Професія: </b> 
            геохімік
          </li>
          <li>
            <b>Нагороди: 2 </b> 
            (Премія Міяке з геохімії, Премія Танака)
          </li>
          <li>
            <b>Відкрито: </b>
            метод вимірювання вмісту діоксиду карбону в морській воді
          </li>
        </ul>
      </section>
    </div>
  );
}