React Front-end Инженер

React Front-end Инженер

Роадмап навыков для прокачки

Для чего нужны props

ReactComponentsСвойства (props)

Основная идея

Props (properties) — это механизм передачи данных от родительского компонента к дочернему. Они делают компоненты переиспользуемыми и настраиваемыми, как параметры функции.

Ключевые аспекты

  • Однонаправленный поток — данные передаются только сверху вниз
  • Read-only — props нельзя изменять внутри компонента
  • Любые типы данных — строки, числа, объекты, массивы, функции, JSX
  • children — специальный prop для вложенного содержимого
  • Деструктуризация — удобный способ извлечения props

Плюсы

  • Компоненты становятся переиспользуемыми
  • Явная передача данных — легко отследить зависимости
  • Поддержка типизации через TypeScript/PropTypes
  • Возможность задать значения по умолчанию

Минусы

  • Prop drilling при глубокой вложенности
  • Много boilerplate при большом количестве props
  • Нужно явно пробрасывать через каждый уровень

Частые ошибки на собеседованиях

  • Путают props и state — props приходят извне, state управляется внутри
  • Пытаются изменить props напрямую
  • Забывают про деструктуризацию и пишут props.name везде
  • Не понимают, что функции тоже можно передавать как props

Введение и проблематика

Props — это сокращение от "properties" (свойства). Это основной механизм передачи данных между компонентами в React.

Props можно сравнить с аргументами функции: родительский компонент "вызывает" дочерний, передавая ему нужные данные через props.

Зачем нужны props?

  • Переиспользование — один компонент Button может отображать разный текст
  • Конфигурация — настройка поведения и внешнего вида компонента
  • Композиция — построение сложных интерфейсов из простых блоков
  • Разделение ответственности — компонент не знает, откуда пришли данные

Базовая теория

Что такое props

Props — это объект, содержащий все атрибуты, переданные компоненту при его использовании:

Code Example 1: Какой объект получит компонент UserCard внутри? Как props передаются при использовании компонента?

tsx
// Передача props
<UserCard name="Иван" age={25} isActive={true} />
 
// Внутри компонента props — это объект:
// { name: "Иван", age: 25, isActive: true }

Типы данных в props

ТипПример
Строка<Button text="Нажми" />
Число<Counter initial={0} />
Boolean<Modal isOpen={true} />
Массив<List items={['a', 'b', 'c']} />
Объект<User data={{ name: 'Ян' }} />
Функция<Button onClick={handleClick} />
JSX<Card header={<Title />} />
Компонент<Route component={Home} />

Практические примеры

Базовая передача props

Code Example 2: Как родительский компонент передаёт данные в дочерний? Как дочерний компонент получает эти данные?

tsx
// Родительский компонент
function App() {
  return (
    <Greeting
      name="Мария"
      message="Добро пожаловать!"
    />
  )
}
 
// Дочерний компонент
function Greeting(props) {
  return (
    <div>
      <h1>{props.message}</h1>
      <p>Привет, {props.name}!</p>
    </div>
  )
}

Деструктуризация props

Code Example 3: В чём разница между этими тремя способами работы с props? Какой способ рекомендуется?

tsx
// Через props.
function Greeting(props) {
  return (
    <div>
      <h1>{props.message}</h1>
      <p>Привет, {props.name}!</p>
    </div>
  )
}

Значения по умолчанию

Code Example 4: Что произойдёт, если не передать variant и size при использовании Button? Какие значения будут использованы?

tsx
// Способ 1: Деструктуризация с дефолтами
function Button({ variant = 'primary', size = 'medium', children }) {
  return (
    <button className={`btn btn-${variant} btn-${size}`}>
      {children}
    </button>
  )
}
 
// Использование
<Button>Обычная кнопка</Button>
<Button variant="danger" size="large">Удалить</Button>

Специальный prop: children

Code Example 5: Что такое children? Откуда берётся его значение в компоненте Card?

tsx
// children — всё, что находится между открывающим и закрывающим тегами
function Card({ title, children }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div className="card-body">
        {children}
      </div>
    </div>
  )
}
 
// Использование
<Card title="Новости">
  <p>Сегодня солнечно!</p>
  <button>Подробнее</button>
</Card>

Spread-оператор для props

Code Example 6: Что делает spread-оператор {...buttonProps}? Какой результат будет эквивалентен?

tsx
// Передача всех props одним объектом
const buttonProps = {
  type: 'submit',
  disabled: false,
  className: 'btn-primary'
}
 
<button {...buttonProps}>Отправить</button>
 
// Эквивалентно:
<button type="submit" disabled={false} className="btn-primary">
  Отправить
</button>

Props vs State

⚠️

Важно понимать разницу между props и state — это фундаментальная концепция React.

ХарактеристикаPropsState
ИсточникРодительский компонентСам компонент
ИзменяемостьТолько для чтенияИзменяемый через setState
НазначениеКонфигурация компонентаВнутренние данные
Кто контролируетРодительСам компонент
Триггер ре-рендераДаДа

Code Example 7: Чем отличаются props от state в этом примере? Кто контролирует данные?

tsx
function Example() {
  // state — внутреннее состояние
  const [count, setCount] = useState(0)
 
  // props передаются извне
  return <Counter value={count} onIncrement={() => setCount(c => c + 1)} />
}
 
function Counter({ value, onIncrement }) {
  // value — prop (read-only)
  // onIncrement — prop (callback)
  return (
    <div>
      <span>{value}</span>
      <button onClick={onIncrement}>+1</button>
    </div>
  )
}

TypeScript типизация

tsx
// Определение интерфейса props
interface UserCardProps {
  name: string
  email: string
  age?: number  // опциональный prop
  avatar?: string
  onEdit?: () => void
}
 
// Использование с типизацией
function UserCard({
  name,
  email,
  age = 18,
  avatar = '/default-avatar.png',
  onEdit
}: UserCardProps) {
  return (
    <div className="user-card">
      <img src={avatar} alt={name} />
      <h3>{name}</h3>
      <p>{email}</p>
      <p>Возраст: {age}</p>
      {onEdit && <button onClick={onEdit}>Редактировать</button>}
    </div>
  )
}

Пограничные кейсы

Boolean props

tsx
// Если передать только имя атрибута, значение будет true
<Button disabled />       // disabled={true}
<Button disabled={true} /> // эквивалентно
 
// Для false нужно явно указать
<Button disabled={false} />

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

tsx
function Layout({ header, sidebar, children }) {
  return (
    <div className="layout">
      <header>{header}</header>
      <aside>{sidebar}</aside>
      <main>{children}</main>
    </div>
  )
}
 
<Layout
  header={<Navigation />}
  sidebar={<Menu />}
>
  <Article />
</Layout>

Render Props паттерн

tsx
// Передача функции, возвращающей JSX
function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 })
 
  return (
    <div onMouseMove={e => setPosition({ x: e.clientX, y: e.clientY })}>
      {render(position)}
    </div>
  )
}
 
<MouseTracker
  render={({ x, y }) => <p>Мышь: {x}, {y}</p>}
/>
🚫

Никогда не мутируйте props! Это нарушит однонаправленный поток данных и приведёт к непредсказуемому поведению.

Code Example 8: Почему Version A неправильна? Как исправить, если нужно изменить данные?

tsx
// ❌ НЕПРАВИЛЬНО
function BadComponent(props) {
  props.name = 'Другое имя' // Мутация props!
  return <span>{props.name}</span>
}
 
// ✅ Если нужно изменить — используйте state
function GoodComponent({ initialName }) {
  const [name, setName] = useState(initialName)
  return <span>{name}</span>
}

Плюсы и минусы

АспектПлюсыМинусы
Передача данныхЯвная и прозрачнаяProp drilling
ТипизацияПолная поддержка TSТребует описания
ОтладкаЛегко отследитьМного уровней
ТестированиеПростая изоляция

Вопросы интервьюера

Q: Что такое props в React?

Props — это объект с данными, передаваемыми от родительского компонента к дочернему. Они делают компоненты переиспользуемыми и настраиваемыми.

Q: Чем отличаются props от state?

Props приходят извне и read-only. State — внутреннее состояние компонента, которое он может изменять.

Q: Что такое children?

Специальный prop, содержащий всё, что передано между открывающим и закрывающим тегами компонента.

Q: Можно ли изменить props?

Нет, props неизменяемы (read-only). Для изменяемых данных используйте state.

Q: Что такое prop drilling?

Проблема, когда props нужно пробрасывать через много уровней компонентов. Решается через Context API или state-менеджеры.


Источники

Code Example 1: Props object

❓ Какой объект получит компонент UserCard внутри? Как props передаются при использовании компонента?

tsx
<UserCard name="Иван" age={25} isActive={true} />

Code Example 2: Basic props passing

❓ Как родительский компонент передаёт данные в дочерний? Как дочерний компонент получает эти данные?

tsx
function App() {
  return (
    <Greeting
      name="Мария"
      message="Добро пожаловать!"
    />
  )
}
 
function Greeting(props) {
  return (
    <div>
      <h1>{props.message}</h1>
      <p>Привет, {props.name}!</p>
    </div>
  )
}

Code Example 3: Props destructuring

❓ В чём разница между этими тремя способами работы с props? Какой способ рекомендуется?

Without destructuring:

tsx
function Greeting(props) {
  return (
    <div>
      <h1>{props.message}</h1>
      <p>Привет, {props.name}!</p>
    </div>
  )
}

Destructuring inside:

tsx
function Greeting(props) {
  const { name, message } = props
 
  return (
    <div>
      <h1>{message}</h1>
      <p>Привет, {name}!</p>
    </div>
  )
}

Destructuring in parameters:

tsx
function Greeting({ name, message }) {
  return (
    <div>
      <h1>{message}</h1>
      <p>Привет, {name}!</p>
    </div>
  )
}

Code Example 4: Default values

❓ Что произойдёт, если не передать variant и size при использовании Button? Какие значения будут использованы?

tsx
function Button({ variant = 'primary', size = 'medium', children }) {
  return (
    <button className={`btn btn-${variant} btn-${size}`}>
      {children}
    </button>
  )
}
 
<Button>Обычная кнопка</Button>
<Button variant="danger" size="large">Удалить</Button>

Code Example 5: Children prop

❓ Что такое children? Откуда берётся его значение в компоненте Card?

tsx
function Card({ title, children }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div className="card-body">
        {children}
      </div>
    </div>
  )
}
 
<Card title="Новости">
  <p>Сегодня солнечно!</p>
  <button>Подробнее</button>
</Card>

Code Example 6: Spread operator

❓ Что делает spread-оператор {...buttonProps}? Какой результат будет эквивалентен?

tsx
const buttonProps = {
  type: 'submit',
  disabled: false,
  className: 'btn-primary'
}
 
<button {...buttonProps}>Отправить</button>

Code Example 7: Props vs State

❓ Чем отличаются props от state в этом примере? Кто контролирует данные?

tsx
function Example() {
  const [count, setCount] = useState(0)
 
  return <Counter value={count} onIncrement={() => setCount(c => c + 1)} />
}
 
function Counter({ value, onIncrement }) {
  return (
    <div>
      <span>{value}</span>
      <button onClick={onIncrement}>+1</button>
    </div>
  )
}

Code Example 8: Props mutation

❓ Почему Version A неправильна? Как исправить, если нужно изменить данные?

Version A:

tsx
function BadComponent(props) {
  props.name = 'Другое имя'
  return <span>{props.name}</span>
}

Version B:

tsx
function GoodComponent({ initialName }) {
  const [name, setName] = useState(initialName)
  return <span>{name}</span>
}