← Назад к вопросам
Как проверить, нужно ли обновлять компонент в зависимости от изменения пропсов?
1.8 Middle🔥 141 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимизация рендеринга при изменении props
В React есть несколько способов проверить, нужно ли обновлять компонент при изменении props.
1. React.memo (рекомендуется)
// ПЛОХО: Компонент рендерится при каждом рендере родителя
export function UserCard({ user, onDelete }) {
console.log('UserCard rendered');
return (
<div>
<h2>{user.name}</h2>
<button onClick={onDelete}>Delete</button>
</div>
);
}
// ХОРОШО: Пропускает рендер если props не изменились
export const UserCard = React.memo(function({ user, onDelete }) {
console.log('UserCard rendered');
return (
<div>
<h2>{user.name}</h2>
<button onClick={onDelete}>Delete</button>
</div>
);
});
// Теперь рендер пропускается если user и onDelete не изменились
2. Кастомное сравнение props
// Если нужна кастомная логика сравнения
export const UserCard = React.memo(
function({ user, onDelete }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Возвращаем true если props равны (пропускаем рендер)
// Возвращаем false если props разные (выполняем рендер)
return (
prevProps.user.id === nextProps.user.id &&
prevProps.onDelete === nextProps.onDelete
);
}
);
// Пример: сравниваем только id, игнорируя другие поля user
export const UserCard = React.memo(
function({ user, onDelete }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Пропускаем рендер только если id одинаковый
return prevProps.user.id === nextProps.user.id;
}
);
3. useMemo для сложных вычислений
import { useMemo } from 'react';
export function Parent({ users }) {
// usersList пересчитывается только если users изменился
const usersList = useMemo(() => {
return users.map(u => ({
...u,
displayName: `${u.firstName} ${u.lastName}`
}));
}, [users]);
return <UserList users={usersList} />;
}
// Без useMemo usersList был бы новый объект на каждый рендер
// С useMemo он остаётся тем же если users не изменился
4. useCallback для функций
import { useCallback } from 'react';
export function Parent() {
// handleDelete переиспользуется если нет зависимостей
const handleDelete = useCallback((id) => {
console.log('Delete:', id);
}, []); // Пустой массив = функция не пересоздаётся
return <UserCard onDelete={handleDelete} />;
}
// Без useCallback новая функция на каждый рендер
// Это вызовет пересчет UserCard даже если логика одна и та же
5. Сравнение props в классовом компоненте
// В классовых компонентах используй shouldComponentUpdate
class UserCard extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// Возвращаем true если нужен рендер, false если пропускаем
return (
this.props.user.id !== nextProps.user.id ||
this.props.count !== nextProps.count
);
}
render() {
return <div>{this.props.user.name}</div>;
}
}
// Или используй PureComponent (shallow compare props и state)
class UserCard extends React.PureComponent {
// Автоматически сравнивает props и state
render() {
return <div>{this.props.user.name}</div>;
}
}
6. Практический пример с useCallback
import { useState, useCallback } from 'react';
export function UserList() {
const [users, setUsers] = useState([]);
// ПЛОХО: Новая функция на каждый рендер
const handleDelete = (id) => {
setUsers(users.filter(u => u.id !== id));
};
// ХОРОШО: Функция переиспользуется
const handleDelete = useCallback((id) => {
setUsers(prev => prev.filter(u => u.id !== id));
}, []);
return (
<div>
{users.map(user => (
<UserCard
key={user.id}
user={user}
onDelete={handleDelete} // Теперь функция стабильна
/>
))}
</div>
);
}
// UserCard.tsx
export const UserCard = React.memo(function({ user, onDelete }) {
console.log('Rendered:', user.id);
return (
<div>
<h2>{user.name}</h2>
<button onClick={() => onDelete(user.id)}>Delete</button>
</div>
);
});
7. Проверка с помощью зависимостей
import { useEffect } from 'react';
export function UserCard({ user, onDelete }) {
// Отслеживаем когда user изменился
useEffect(() => {
console.log('User changed:', user.id);
}, [user]); // Зависимость user
// Отслеживаем когда ID изменился
useEffect(() => {
console.log('User ID changed:', user.id);
}, [user.id]); // Зависимость только от ID
return <div>{user.name}</div>;
}
8. Библиотека для глубокого сравнения
import { isEqual } from 'lodash';
// Кастомное сравнение со скрытыми полями
export const UserCard = React.memo(
function({ user, onDelete }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// isEqual глубоко сравнивает объекты
return isEqual(prevProps.user, nextProps.user);
}
);
// ВНИМАНИЕ: глубокое сравнение медленнее, чем поверхностное
// Используй только если необходимо
Лучшие практики
// 1. ИСПОЛЬЗУЙ REACT.MEMO для простых компонентов
export const Button = React.memo(function({ label, onClick }) {
return <button onClick={onClick}>{label}</button>;
});
// 2. ИСПОЛЬЗУЙ USECALLBACK для функций
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
// 3. ИСПОЛЬЗУЙ USEMEMO для дорогих вычислений
const filtered = useMemo(() => {
return items.filter(x => x.value > 100);
}, [items]);
// 4. ПРАВИЛЬНЫЕ ЗАВИСИМОСТИ в useEffect
useEffect(() => {
// код
}, [dependency1, dependency2]);
// 5. ИЗБЕГАЙ СОЗДАНИЯ НОВЫХ ОБЪЕКТОВ В PROPS
// ПЛОХО:
<UserCard user={{ id: 1, name: 'John' }} />
// Новый объект на каждый рендер!
// ХОРОШО:
const user = useMemo(() => ({ id: 1, name: 'John' }), []);
<UserCard user={user} />
Ответ: используй React.memo для автоматического сравнения props, useCallback для стабильных функций, и useMemo для дорогих вычислений. Это позволяет React оптимизировать рендеринг.