Props (свойства) — это данные, передаваемые от родительского компонента к дочернему в React. Они иммутабельны (неизменяемы) по дизайну для обеспечения предсказуемого потока данных и предотвращения побочных эффектов.
✅ Почему props иммутабельны:
❌ Что нельзя делать с props:
props.name = 'new name')Ключевое правило: Компонент может читать props, но не может их изменять — за это отвечает родительский компонент.
Props (сокращение от properties) — это один из фундаментальных концептов React, представляющий собой объект с данными, который передается от родительского компонента к дочернему. Понимание иммутабельности props критически важно для правильной работы с React-приложениями.
Props — это способ передачи данных от родительского компонента к дочернему:
// Родительский компонент передает данные
function App() {
return (
<div>
<UserProfile
name="Александр"
age={25}
hobbies={['программирование', 'музыка']}
/>
<UserSettings theme="dark" notifications={true} />
</div>
);
}
// Дочерний компонент получает props
function UserProfile(props) {
return (
<div>
<h1>{props.name}</h1>
<p>Возраст: {props.age}</p>
<ul>
{props.hobbies.map(hobby => <li key={hobby}>{hobby}</li>)}
</ul>
</div>
);
}
// С деструктуризацией
function UserSettings({ theme, notifications }) {
return (
<div>
<p>Тема: {theme}</p>
<p>Уведомления: {notifications ? 'вкл' : 'выкл'}</p>
</div>
);
}React использует однонаправленный поток данных (unidirectional data flow), где данные передаются сверху вниз:
// ❌ Неправильно - попытка изменить props
function ChildComponent(props) {
// props.name = 'Новое имя'; // ❌ Ошибка! Props нельзя изменять
return <div>Привет, {props.name}!</div>;
}
// ✅ Правильно - компонент просто использует props
function ChildComponent({ name }) {
return <div>Привет, {name}!</div>;
}
// Если нужно изменить данные, используйте состояние
function ParentComponent() {
const [name, setName] = useState('Александр');
return (
<div>
<ChildComponent name={name} />
<button onClick={() => setName('Анна')}>
Изменить имя
</button>
</div>
);
}Иммутабельность props предотвращает побочные эффекты и непредсказуемое поведение:
// ❌ Опасный код - изменение props может повлиять на другие компоненты
function BadComponent(props) {
// props.user.name = 'Новое имя'; // ❌ Это может повлиять на родительский компонент!
return <div>{props.user.name}</div>;
}
// ✅ Безопасный код - создание копии для изменений
function GoodComponent({ user }) {
// Создаем копию для безопасных изменений
const modifiedUser = { ...user, name: 'Новое имя' };
return <div>{modifiedUser.name}</div>;
}Иммутабельность упрощает отладку и понимание потока данных:
// Легко отследить, откуда пришли данные
function UserCard({ user, onEdit }) {
// Мы точно знаем, что user пришел сверху и не изменится внутри компонента
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
<button onClick={() => onEdit(user)}>
Редактировать
</button>
</div>
);
}React использует сравнение ссылок для оптимизации рендеринга:
// React может эффективно сравнивать props
function ExpensiveComponent({ data }) {
// Если ссылка на data не изменилась, компонент не перерендерится
const processedData = useMemo(() => {
return data.map(item => processItem(item));
}, [data]);
return (
<div>
{processedData.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
// Если бы props были мутабельны, React не мог бы полагаться на сравнение ссылокКомпоненты могут свободно читать props:
// Функциональный компонент
function WelcomeMessage({ name, greeting = 'Привет' }) {
return <h1>{greeting}, {name}!</h1>;
}
// С деструктуризацией и значениями по умолчанию
function UserCard({
user: { name, email, avatar },
showAvatar = true,
onEdit = () => {}
}) {
return (
<div className="user-card">
{showAvatar && <img src={avatar} alt={name} />}
<h2>{name}</h2>
<p>{email}</p>
<button onClick={onEdit}>Редактировать</button>
</div>
);
}Родительские компоненты передают данные через атрибуты:
function App() {
const userData = {
name: 'Александр',
email: 'alex@example.com',
avatar: '/avatar.jpg'
};
const handleEdit = (user) => {
console.log('Редактирование пользователя:', user);
};
return (
<div>
{/* Передача отдельных props */}
<WelcomeMessage name="Анна" greeting="Здравствуйте" />
{/* Передача объекта как spread */}
<UserCard
user={userData}
showAvatar={true}
onEdit={handleEdit}
/>
{/* Использование spread оператора */}
<UserProfile {...userData} />
</div>
);
}Когда нужно изменить данные из props, используйте состояние:
// ❌ Неправильно
function Counter({ initialValue }) {
let count = initialValue; // ❌ Локальная переменная не будет обновляться
return (
<div>
<p>Счетчик: {count}</p>
<button onClick={() => count++}> {/* ❌ Не работает */}
Увеличить
</button>
</div>
);
}
// ✅ Правильно
function Counter({ initialValue }) {
const [count, setCount] = useState(initialValue);
return (
<div>
<p>Счетчик: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}// ❌ Неправильно - мутирование объекта из props
function TodoItem({ todo, onUpdate }) {
const toggleComplete = () => {
// todo.completed = !todo.completed; // ❌ Не мутируйте props!
onUpdate(todo);
};
return (
<div className={todo.completed ? 'completed' : ''}>
<span>{todo.text}</span>
<button onClick={toggleComplete}>
{todo.completed ? 'Отменить' : 'Завершить'}
</button>
</div>
);
}
// ✅ Правильно - создание нового объекта
function TodoItem({ todo, onUpdate }) {
const toggleComplete = () => {
// Создаем новый объект с обновленным значением
const updatedTodo = { ...todo, completed: !todo.completed };
onUpdate(updatedTodo);
};
return (
<div className={todo.completed ? 'completed' : ''}>
<span>{todo.text}</span>
<button onClick={toggleComplete}>
{todo.completed ? 'Отменить' : 'Завершить'}
</button>
</div>
);
}Особый вид props — children, который содержит содержимое компонента:
// Компонент, который оборачивает содержимое
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">
{children} {/* Содержимое переданное между тегами */}
</div>
</div>
);
}
// Использование
function App() {
return (
<Card title="Мой профиль">
<p>Это содержимое карточки</p>
<button>Действие</button>
</Card>
);
}Функции часто передаются как props для обратного вызова:
// Компонент кнопки с настраиваемым обработчиком
function CustomButton({ onClick, children, variant = 'primary' }) {
// Никогда не вызывайте onClick напрямую в теле компонента!
// onClick(); // ❌ Это вызовет функцию при каждом рендере
return (
<button
className={`btn btn-${variant}`}
onClick={onClick} // ✅ Передаем функцию как обработчик события
>
{children}
</button>
);
}
// Использование
function App() {
const handleClick = () => {
console.log('Кнопка нажата!');
};
return (
<div>
<CustomButton onClick={handleClick}>
Нажми меня
</CustomButton>
</div>
);
}// ❌ Неправильно
function UserProfile(props) {
props.name = 'Новое имя'; // ❌ Ошибка!
return <div>{props.name}</div>;
}
// ✅ Правильно
function UserProfile({ name }) {
const [localName, setLocalName] = useState(name);
return (
<div>
<span>{localName}</span>
<button onClick={() => setLocalName('Новое имя')}>
Изменить
</button>
</div>
);
}// ❌ Неправильно
function TodoList({ todos, onTodosChange }) {
const addTodo = (text) => {
todos.push({ id: Date.now(), text, completed: false }); // ❌ Мутирование!
onTodosChange(todos);
};
return (
<div>
{todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
<button onClick={() => addTodo('Новая задача')}>
Добавить
</button>
</div>
);
}
// ✅ Правильно
function TodoList({ todos, onTodosChange }) {
const addTodo = (text) => {
const newTodos = [...todos, { id: Date.now(), text, completed: false }]; // ✅ Новый массив
onTodosChange(newTodos);
};
return (
<div>
{todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
<button onClick={() => addTodo('Новая задача')}>
Добавить
</button>
</div>
);
}Props — это данные, передаваемые от родительского компонента к дочернему в React, которые являются иммутабельными по дизайну:
✅ Почему props иммутабельны:
❌ Что нельзя делать:
Ключевые моменты:
Понимание иммутабельности props — фундаментальный навык React-разработчика, который помогает писать предсказуемый, поддерживаемый и эффективный код.
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪