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

Как компоненту передать сразу все props?

2.2 Middle🔥 131 комментариев
#React

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

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

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

Передача всех props компоненту в React

1. Оператор распаковки (...) для props

Оператор spread позволяет передать все свойства объекта как отдельные props:

// Простой объект с свойствами
const buttonProps = {
  type: 'button',
  className: 'btn btn-primary',
  onClick: handleClick,
  disabled: false
};

// Передаем все props сразу
<button {...buttonProps}>Нажми меня</button>

// Эквивалентно:
<button
  type="button"
  className="btn btn-primary"
  onClick={handleClick}
  disabled={false}
>
  Нажми меня
</button>

2. Распаковка props в компоненте

// Компонент получает объект props
function Button(props) {
  return (
    <button {...props}>
      {props.children}
    </button>
  );
}

// Использование
const config = {
  className: 'primary',
  onClick: () => alert('Clicked'),
  type: 'submit'
};

<Button {...config}>Отправить</Button>

3. Деструктуризация + распаковка

// Выделяем известные props, остальное в rest
function Input({ value, onChange, ...rest }) {
  return (
    <input
      value={value}
      onChange={onChange}
      {...rest} // Все оставшиеся props (placeholder, type, required и т.д.)
    />
  );
}

// Использование
const inputProps = {
  placeholder: 'Введите текст',
  type: 'email',
  required: true,
  maxLength: 50,
  aria-label: 'Email input'
};

<Input value={email} onChange={handleChange} {...inputProps} />

4. Расширение props для дочерних компонентов

function Card({ title, children, ...props }) {
  return (
    <div className="card" {...props}>
      <div className="card-header">{title}</div>
      <div className="card-body">{children}</div>
    </div>
  );
}

// Использование
<Card
  title="Информация"
  id="user-card"
  data-testid="card-1"
  className="custom-card"
>
  Содержимое карточки
</Card>

5. Объединение props из разных источников

function Button({ variant = 'primary', size = 'md', ...rest }) {
  // Базовые стили в зависимости от варианта
  const baseClass = `btn btn-${variant} btn-${size}`;
  
  // Объединяем с переданным классом
  const finalClass = rest.className
    ? `${baseClass} ${rest.className}`
    : baseClass;
  
  return (
    <button {...rest} className={finalClass}>
      {rest.children}
    </button>
  );
}

// Использование
<Button
  variant="secondary"
  size="lg"
  className="custom-button"
  onClick={handleClick}
>
  Большая вторичная кнопка
</Button>

6. TypeScript типизация props

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary';
  size?: 'sm' | 'md' | 'lg';
}

function Button({
  variant = 'primary',
  size = 'md',
  className,
  ...rest
}: ButtonProps) {
  const classes = `btn btn-${variant} btn-${size} ${className || ''}`;
  
  return (
    <button className={classes} {...rest} />
  );
}

// Типизирование с правильной проверкой
const props = {
  type: 'submit' as const,
  onClick: () => {},
  disabled: false
};

<Button {...props}>Отправить</Button>

7. Передача props в HOC (Higher-Order Component)

// HOC, который оборачивает компонент
function withLoading(Component) {
  return function WrappedComponent({ isLoading, ...rest }) {
    if (isLoading) {
      return <div>Загрузка...</div>;
    }
    
    // Передаем все остальные props оригинальному компоненту
    return <Component {...rest} />;
  };
}

// Использование
const UserList = ({ users }) => (
  <ul>
    {users.map(user => <li key={user.id}>{user.name}</li>)}
  </ul>
);

const UserListWithLoading = withLoading(UserList);

<UserListWithLoading isLoading={false} users={[...]} />

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

function FormField({ required, ...rest }) {
  const props = {
    ...rest,
    required // Явно указываем required
  };
  
  // Добавляем aria-required только если required=true
  if (required) {
    props['aria-required'] = true;
  }
  
  return <input {...props} />;
}

// Или более элегантно:
function FormField({ required, ...rest }) {
  return (
    <input
      {...rest}
      required={required}
      aria-required={required || undefined}
    />
  );
}

9. React.forwardRef с распаковкой props

const Input = React.forwardRef(({ label, error, ...rest }, ref) => {
  return (
    <div className="form-group">
      {label && <label>{label}</label>}
      <input ref={ref} {...rest} />
      {error && <span className="error">{error}</span>}
    </div>
  );
});

// Использование
const inputRef = useRef(null);

<Input
  ref={inputRef}
  label="Email"
  type="email"
  placeholder="user@example.com"
  error={emailError}
/>

10. Использование cn() для условных классов

import { cn } from '@/lib/utils';

function Button({ variant = 'primary', size = 'md', className, ...rest }) {
  return (
    <button
      className={cn(
        'btn',
        {
          'btn-primary': variant === 'primary',
          'btn-secondary': variant === 'secondary',
          'btn-sm': size === 'sm',
          'btn-md': size === 'md',
          'btn-lg': size === 'lg'
        },
        className
      )}
      {...rest}
    />
  );
}

11. Compound Components паттерн

function Form({ ...props }) {
  return <form {...props} />;
}

function FormGroup({ ...props }) {
  return <fieldset {...props} />;
}

function FormInput({ label, ...props }) {
  return (
    <div className="form-group">
      {label && <label>{label}</label>}
      <input {...props} />
    </div>
  );
}

function FormButton({ ...props }) {
  return <button type="submit" {...props} />;
}

// Использование
<Form onSubmit={handleSubmit}>
  <FormGroup>
    <FormInput label="Имя" name="name" type="text" />
    <FormInput label="Email" name="email" type="email" />
    <FormButton>Отправить</FormButton>
  </FormGroup>
</Form>

12. Практический пример: обертка над HTML элементом

interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
  variant?: 'default' | 'elevated' | 'outlined';
  padding?: 'sm' | 'md' | 'lg';
}

function Card({
  variant = 'default',
  padding = 'md',
  className,
  children,
  ...rest
}: CardProps) {
  return (
    <div
      className={cn(
        'rounded-lg',
        {
          'shadow-none': variant === 'default',
          'shadow-md': variant === 'elevated',
          'border': variant === 'outlined',
          'p-2': padding === 'sm',
          'p-4': padding === 'md',
          'p-6': padding === 'lg'
        },
        className
      )}
      {...rest}
    >
      {children}
    </div>
  );
}

// Использование
<Card
  variant="elevated"
  padding="lg"
  id="user-card"
  data-testid="card"
  onClick={handleClick}
>
  Содержимое карточки
</Card>

Что ВАЖНО помнить

  1. Порядок имеет значение: если передать пропс дважды, последний перезапишет предыдущий:
<Component {...props} onClick={newHandler} /> // newHandler будет использован
<Component onClick={newHandler} {...props} /> // props.onClick будет использован
  1. Не забывай про children: при использовании rest параметров children туда не попадет:
function Component({ ...rest }) {
  // rest содержит все props КРОМЕ children
  return <div>{rest.children}</div>; // undefined!
}

// Правильно:
function Component({ children, ...rest }) {
  return <div {...rest}>{children}</div>;
}
  1. Типизируй правильно: используй HTMLAttributes<T> для получения типов всех HTML атрибутов

Итоги

Spread оператор (...) для props:

  • Упрощает передачу множества пропсов
  • Работает для конфигурирования компонентов
  • Стандартный паттерн в React
  • Комбинируй с деструктуризацией для выделения известных props
  • Помни про порядок передачи для избежания конфликтов
Как компоненту передать сразу все props? | PrepBro