← Назад к вопросам

Какой оператор используется для передачи всех props компоненту?

2.0 Middle🔥 171 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Spread оператор (...) для передачи props в React

Spread оператор (...) используется для распаковки всех свойств объекта в отдельные props компоненту. Это мощный инструмент для работы с props в React.

Основное применение

Передача отдельных props

const user = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  age: 25
};

// Без spread оператора (многословно)
function UserCard() {
  return (
    <User
      id={user.id}
      name={user.name}
      email={user.email}
      age={user.age}
    />
  );
}

// С spread оператором (компактно)
function UserCard() {
  return <User {...user} />;
}

// Результат одинаков:
// <User id={1} name="Alice" email="alice@example.com" age={25} />

Как это работает

// Spread распаковывает все ключи объекта в отдельные props

const obj = { a: 1, b: 2, c: 3 };

// {...obj} эквивалентно: a={1} b={2} c={3}
<Component {...obj} />

// Это только синтаксический сахар, реально происходит:
<Component a={1} b={2} c={3} />

Комбинирование с другими props

const baseProps = {
  type: 'text',
  placeholder: 'Enter text',
  disabled: false
};

function Input() {
  return (
    <input
      {...baseProps}
      value="custom value"
      onChange={handleChange}
    />
  );
}

// Результат:
// <input
//   type="text"
//   placeholder="Enter text"
//   disabled={false}
//   value="custom value"
//   onChange={handleChange}
// />

// Порядок важен! Пропс идущий после spread перетирает spread
function Input2() {
  return (
    <input
      disabled={true}  // Это перетрёт disabled: false из baseProps
      {...baseProps}
    />
  );
}

// Результат:
// <input disabled={false} /> (disabled из spread перетёр disabled={true})

Фильтрование и манипуляция props

Rest параметр (обратное от spread)

// Rest: собрать оставшиеся props
interface Props {
  title: string;
  theme: 'light' | 'dark';
  // и ещё много других props
}

function Card({ title, theme, ...rest }: Props) {
  // title и theme извлечены отдельно
  // rest содержит все остальные props
  
  return (
    <div className={`card card-${theme}`} {...rest}>
      <h2>{title}</h2>
    </div>
  );
}

const extraProps = {
  className: 'custom-class',
  data-testid: 'card',
  onClick: () => {}
};

<Card title="Hello" theme="dark" {...extraProps} />
// className='custom-class' и остальные extraProps попадут в rest

Извлечение и передача

interface ButtonProps {
  variant?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
  children: React.ReactNode;
}

function Button({ variant = 'primary', size = 'medium', ...htmlProps }: ButtonProps) {
  const className = `btn btn-${variant} btn-${size}`;
  
  return (
    <button className={className} {...htmlProps}>
      {children}
    </button>
  );
}

// Использование:
<Button
  variant="primary"
  size="large"
  onClick={() => alert('Clicked')}
  disabled={false}
  data-testid="submit-button"
>
  Submit
</Button>

// {...htmlProps} передаст onClick, disabled и data-testid на нативный button

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

1. Обёртка над компонентом (Wrapper)

// Часто нужно обернуть компонент, но пробросить все его props

interface TextInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  error?: string;
}

function TextInput({ label, error, ...inputProps }: TextInputProps) {
  return (
    <div className="input-group">
      {label && <label>{label}</label>}
      <input {...inputProps} />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

// Все HTML input props автоматически переносятся
<TextInput
  label="Email"
  error="Invalid email"
  type="email"
  placeholder="your@email.com"
  value={email}
  onChange={handleChange}
  disabled={isLoading}
  required
/>

2. Передача props на разные элементы

interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {
  title: string;
}

function Container({ title, ...divProps }: ContainerProps) {
  return (
    <div {...divProps}>
      <h1>{title}</h1>
      {/* Остальное содержимое */}
    </div>
  );
}

// Все HTML атрибуты (className, id, data-*, и т.д.) работают
<Container
  title="My App"
  className="main-container"
  id="root"
  data-testid="app"
/>

3. Передача props в вложенные компоненты

interface CardProps {
  header?: React.ReactNode;
  headerProps?: React.HTMLAttributes<HTMLDivElement>;
  body?: React.ReactNode;
  footer?: React.ReactNode;
  footerProps?: React.HTMLAttributes<HTMLDivElement>;
}

function Card({ header, headerProps, body, footer, footerProps }: CardProps) {
  return (
    <div className="card">
      {header && <div className="card-header" {...headerProps}>{header}</div>}
      <div className="card-body">{body}</div>
      {footer && <div className="card-footer" {...footerProps}>{footer}</div>}
    </div>
  );
}

// Использование:
<Card
  header="Title"
  headerProps={{ className: 'custom-header', style: { color: 'blue' } }}
  body="Content"
  footer="Footer"
  footerProps={{ onClick: () => {} }}
/>

4. Условная передача props

interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
  isSubmitting?: boolean;
}

function Form({ isSubmitting, ...formProps }: FormProps) {
  const conditionalProps = isSubmitting ? { disabled: true } : {};
  
  return (
    <form {...formProps} {...conditionalProps}>
      {/* Содержимое */}
    </form>
  );
}

// Если isSubmitting=true, все input-ы будут disabled

Распространённые ошибки

1. Spread в props с деструктуризацией

// Ошибка: spread применён к параметру, а не объекту
function Button({ ...props }) {
  return <button {...props} />;
}

// Это работает, но лучше быть явным
function Button(props) {
  return <button {...props} />;
}

// Или ещё лучше:
function Button({ children, ...htmlProps }) {
  return <button {...htmlProps}>{children}</button>;
}

2. Изменение порядка props

// Плохо: критичный props может быть перетёрт
const defaultProps = { disabled: false, type: 'button' };

function Button({ ...customProps }) {
  return (
    <button
      {...customProps}
      disabled={false} // Может быть перетёрт customProps.disabled
    />
  );
}

// Хорошо: критичный props идёт в конце
function Button({ ...customProps }) {
  return (
    <button
      disabled={false}
      {...customProps}
    />
  );
}

3. Spread с нежелательными props

// Проблема: нежелательные props попадают на элемент
const obj = {
  isActive: true,
  internal: 'data',
  onClick: () => {},
  style: { color: 'red' }
};

// Плохо
function Component({ ...props }) {
  return <button {...props} />; // isActive и internal попадут на button!
}

// Хорошо
function Component({ isActive, internal, ...htmlProps }) {
  return (
    <button className={isActive ? 'active' : ''} {...htmlProps}>
      {internal}
    </button>
  );
}

Производительность

// Spread оператор создаёт новый объект
const user = { id: 1, name: 'Alice' };
const spreadUser = { ...user };

console.log(user === spreadUser); // false (разные объекты)

// В React это может вызвать лишние re-renders
function Parent() {
  const props = { a: 1, b: 2 }; // Создаётся при каждом render
  return <Child {...props} />; // Child re-render на каждый render Parent
}

// Решение: мемоизация
const Parent = () => {
  const props = useMemo(() => ({ a: 1, b: 2 }), []);
  return <Child {...props} />;
};

Сравнение с другими подходами

// Способ 1: Spread оператор
function Button({ variant, ...rest }) {
  return <button className={`btn-${variant}`} {...rest} />;
}

// Способ 2: Явная передача (verbose)
function Button({ variant, className, onClick, disabled, ...rest }) {
  return (
    <button
      className={`btn-${variant} ${className}`}
      onClick={onClick}
      disabled={disabled}
      {...rest}
    />
  );
}

// Способ 3: Вспомогательная функция (для сложных случаев)
const getButtonProps = (variant) => ({
  className: `btn-${variant}`,
  type: 'button'
});

function Button({ variant, ...rest }) {
  return <button {...getButtonProps(variant)} {...rest} />;
}

Итог

Spread оператор (...) в React используется для:

  • Передачи всех props компоненту без перечисления каждого
  • Комбинирования объектов props
  • Фильтрования нужных props через rest параметр
  • Создания гибких wrapper компонентов
  • Пробрасывания HTML атрибутов на нативные элементы

Best practices:

  • Используй spread для переноса HTML props на нативные элементы
  • Извлекай нужные props через деструктуризацию, остальное через rest
  • Помни о порядке: props в конце могут перетереть spread
  • Избегай spread с лишними props (сначала отфильтруй)
  • Мемоизируй объекты props если передаёшь их как зависимости

Правило большого пальца:

// Хорошо
function Component({ specificProp, ...htmlProps }) {
  return <div {...htmlProps}>{specificProp}</div>;
}

// Не очень
function Component(props) {
  return <div {...props} />;
}