Передача пропсов в компонент
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="Линь Ланьинг" 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: 'Линь Ланьинг', imageId: '1bX5QH6' }}
size={100}
/>
);
}
Теперь вы можете прочитать эти пропсы внутри компонента 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: 'Кацуко Сарухаси', imageId: 'YfeOqp2' }} /> <Avatar size={80} person={{ name: 'Аклилу Лемма', imageId: 'OKS67lh' }} /> <Avatar size={50} person={{ name: 'Линь Ланьинг', imageId: '1bX5QH6' }} /> </div> ); }
Пропсы позволяют вам думать о родительском и дочернем компонентах как об отдельных сущностях. К примеру, вы можете изменить проп person
или size
внутри Profile
и не думать о том, как Avatar
их использует. Аналогично, вы можете изменить логику использования этих пропсов внутри Avatar
, не заглядывая в Profile
.
Вы можете думать о пропсах как о ручках регулировки прибора, которые служат для настройки вашего компонента. Они выполняют ту же роль, что и аргументы для функций. Фактически, пропсы и есть аргумент для вашего компонента! Функция React-компонента принимает единственный аргумент — объект с пропсами
:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
Обычно вам не нужен объект props
сам по себе, поэтому вы будете деструктурировать его в отдельные пропсы.
Определение значения по умолчанию для пропа
Если вы хотите дать пропу значение по умолчанию, когда значение не было определено, вы можете это сделать при помощи деструктуризации, добавив =
и значение по умолчанию после параметра:
function Avatar({ person, size = 100 }) {
// ...
}
Теперь, если <Avatar person={...} />
будет использован без пропа size
, size
примет значение 100
.
Значение по умолчанию используется, только если проп size
пропущен или передан size={undefined}
. Но если вы передаёте size={null}
или size={0}
, значение по умолчанию не будет использовано.
Передача пропсов в JSX с оператором расширения
Иногда пропсы передаются сквозь компонент:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
В этом нет ничего плохого и такое дублирование может быть уместным. Но в то же время хочется писать код лаконичнее. Некоторые компоненты передают все их пропсы дочерним компонентам так, как это делает Profile
с Avatar
. Так как непосредственно сам компонент не использует пропсы, имеет смысл использовать более краткую запись и оператор расширения:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
Так вы можете передать все пропсы компонента Profile
в компонент Avatar
без перечисления их имён.
Используйте оператор расширения сдержанно. Если вы используете этот оператор в каждом компоненте, то, скорее всего, что-то пошло не так. Часто это говорит о том, что вам нужно разделить компоненты и передать дочерние через 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: 'Кацуко Сарухаси', imageId: 'YfeOqp2' }} /> </Card> ); }
Попробуйте поменять <Avatar>
внутри <Card>
на любой текст и посмотреть как компонент Card
оборачивает содержимое. Ему необязательно «знать» что рендерится внутри него. Вы ещё увидите множество применений этого гибкого паттерна.
Вы можете представить себе компонент с пропом children
в виде «эклера», в который родительский компонент «добавляет начинку» через JSX. Вы будете часто использовать проп children
для компонентов-обёрток: панелей, гридов и т.д.
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
или пропущен. - Вы можете передать все пропсы в другой компонент
<Avatar {...props} />
с помощью оператора расширения, но не злоупотребляйте этой возможностью! - Вложенный JSX, например
<Card><Avatar /></Card>
, будет передан в компонентCard
в специальном пропе с именемchildren
. - Пропсы можно только читать: при каждом рендере компонент получает новую версию пропсов.
- Вы не можете менять пропсы. Используйте состояние компонента, если вам нужно добавить интерактивность.
Challenge 1 of 3: Выделение компонента
Компонент Gallery
содержит очень похожую разметку для двух профилей. Выделите компонент Profile
, чтобы не дублировать код. Вам нужно определить пропсы, которые вы передадите в этот компонент.
import { getImageUrl } from './utils.js'; export default function Gallery() { return ( <div> <h1>Выдающиеся учёные</h1> <section className="profile"> <h2>Мария Склодовская-Кюри</h2> <img className="avatar" src={getImageUrl('szV5sdG')} alt="Мария Склодовская-Кюри" width={70} height={70} /> <ul> <li> <b>Профессия: </b> физик и химик </li> <li> <b>Награды: 4 </b> (Нобелевская премия по физике, Нобелевская премия по химии, медаль Дэви, медаль Маттеуччи) </li> <li> <b>Открытия: </b> полоний (элемент) </li> </ul> </section> <section className="profile"> <h2>Кацуко Сарухаси</h2> <img className="avatar" src={getImageUrl('YfeOqp2')} alt="Кацуко Сарухаси" width={70} height={70} /> <ul> <li> <b>Профессия: </b> геохимик </li> <li> <b>Награды: 2 </b> (Премия Мияке по геохимии, Премия Танака) </li> <li> <b>Открытия: </b> метод измерения углекислого газа в морской воде </li> </ul> </section> </div> ); }