← Назад к вопросам
Какой оператор используется для передачи всех 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} />;
}