В чем разница между перерендером при смене ключа и при смене пропса?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Key vs Props: различие в перерендере
Это очень важная тема для оптимизации производительности React. Давай разберёмся, чем различаются перерендеры при смене ключа и смене props.
Props: изменение влияет на логику компонента
Props — это параметры компонента, которые определяют его поведение и внешний вид. Когда props меняются, компонент перерендеривается и должен обновиться.
function UserCard({ userId, userName }) {
console.log('Rendering UserCard');
return (
<div>
<h2>{userName}</h2>
<p>ID: {userId}</p>
</div>
);
}
// При смене userId или userName — перерендер
<UserCard userId={1} userName="John" />
// Потом меняем:
<UserCard userId={2} userName="Jane" /> // Перерендер!
Что происходит при смене props:
- Компонент получает новые значения
- React вызывает функцию компонента с новыми props
- Код внутри функции выполняется заново
- Возвращается новый JSX
- React сравнивает старый и новый JSX (reconciliation)
- DOM обновляется, если что-то изменилось
Key: определяет идентичность элемента
Key — это специальный атрибут, который говорит React'у: "Это тот же элемент, даже если позиция изменилась". Key используется в списках.
function UsersList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Если key есть
<li key="user-1">John</li>
<li key="user-2">Jane</li>
// React знает: первый элемент — это всегда user-1
// Даже если его переместить или удалить другой элемент
Проблема: без key
function UsersList({ users }) {
return (
<ul>
{users.map((user, index) => (
<li key={index}> {/* ПЛОХО! */}
{user.name}
<input type="text" />
</li>
))}
</ul>
);
}
// Если удалить первого пользователя:
// До: [John, Jane, Bob]
// После: [Jane, Bob]
//
// БЕЗ key React думает:
// - Первый элемент теперь Jane (был John)
// - Но это всё ещё первый элемент (index=0)
// - React переиспользует компонент!
// - Input СОХРАНЯЕТ ввод для user-1 (что неправильно)
Правильно: с key
function UsersList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}> {/* ХОРОШО */}
{user.name}
<input type="text" />
</li>
))}
</ul>
);
}
// Если удалить первого пользователя:
// До: [user-1: John, user-2: Jane, user-3: Bob]
// После: [user-2: Jane, user-3: Bob]
//
// С key React знает:
// - key='user-1' удалён
// - key='user-2' остался (это Jane)
// - key='user-3' остался (это Bob)
// - Input для user-2 остаётся в своём состоянии
Визуальное сравнение
ОРИГИНАЛЬНЫЙ СПИСОК:
ui-1: <Input value="searching for John" /> - User: John
ui-2: <Input value="" /> - User: Jane
ui-3: <Input value="" /> - User: Bob
УДАЛИМ ДЖОНА из данных:
[Jane, Bob]
БЕЗ key (используется index):
ui-1: <Input value="searching for John" /> - User: Jane ← НЕПРАВИЛЬНО!
ui-2: <Input value="" /> - User: Bob
Input сохранил текст, но теперь это свързано с Jane!
С key:
ui-2: <Input value="" /> - User: Jane ← ПРАВИЛЬНО!
ui-3: <Input value="" /> - User: Bob
Input для Jane чист (новый), как и ожидается
Перерендер при смене key
Если сам key изменяется, React создаст новый компонент с нуля:
function Message({ messageId }) {
const [text, setText] = useState('initial');
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
</div>
);
}
// Родитель меняет key
<Message key={messageId} messageId={messageId} />
// Если messageId меняется (скажем, с 1 на 2):
// key меняется с "1" на "2"
// React удаляет старый компонент полностью
// React создаёт новый компонент
// state обнуляется! (input пуст)
Часто используется для сброса состояния:
function Form() {
const [userId, setUserId] = useState(1);
// При смене userId форма полностью перестраивается
return <UserEditForm key={userId} userId={userId} />;
}
// Когда userId меняется:
// - key меняется
// - React удаляет старый Form
// - React создаёт новый Form
// - Все input'ы очищены
Сравнение перерендеров
| Сценарий | Props меняется | Key меняется | |----------|----------------|--____________| | Компонент пересоздаётся? | Нет, переиспользуется | ДА, полностью пересоздаётся | | State сохраняется? | Да, если props не влияет | Нет, state обнуляется | | Элементы DOM переиспользуются? | Да | Нет, удаляются и создаются новые | | Когда использовать? | Обычные изменения | Когда нужен сброс состояния |
Пример: значение ввода
function TextInput({ text }) {
return <input value={text} onChange={...} />;
}
// Props меняется
<TextInput text="initial" />
// Потом
<TextInput text="updated" /> // Input обновляется, но остаётся сфокусирован
// Key меняется
<TextInput key="1" text="initial" />
// Потом
<TextInput key="2" text="updated" /> // Новый input! Фокус потеряется
Реальный пример: таб страниц
function TabPage({ tabId }) {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
// Восстанавливаем позицию скролла при смене таба
}, [tabId]);
return <div>Tab {tabId}</div>;
}
// БЕЗ key (неправильно)
<TabPage tabId={1} />
<TabPage tabId={2} />
// React переиспользует компонент, нужны useEffect'ы для очистки
// С key (правильно)
<TabPage key={1} tabId={1} />
<TabPage key={2} tabId={2} />
// React создаёт новый компонент для каждого таба
Перерендер с props: подробно
function Counter({ count }) {
console.log('Rendering Counter');
return <div>{count}</div>;
}
// count меняется с 0 на 1
// React:
// 1. Видит, что props изменился
// 2. Вызывает функцию Counter с новыми props
// 3. console.log выполняется (видим "Rendering Counter")
// 4. Возвращает новый JSX: <div>1</div>
// 5. DOM обновляется: текст меняется с "0" на "1"
Перерендер с key: подробно
function Item() {
const [state, setState] = useState('initial');
console.log('Item created');
return <div>{state}</div>;
}
// key меняется с "1" на "2"
// React:
// 1. Видит, что key изменился
// 2. Удаляет старый компонент (вызывает cleanup эффекты)
// 3. Создаёт совершенно новый компонент
// 4. console.log выполняется (видим "Item created")
// 5. state инициализируется: 'initial'
// 6. Новый DOM элемент создаётся
Оптимизация: memo с props
const UserCard = memo(function UserCard({ userId }) {
console.log('UserCard rendered');
return <div>User {userId}</div>;
});
// Если userId не меняется, перерендер НЕ происходит
<UserCard userId={1} />
<UserCard userId={1} /> // Нет перерендера! console.log не выполняется
// Если userId меняется
<UserCard userId={2} /> // Перерендер! console.log выполняется
Оптимизация: ключ для сброса
function ResetableForm({ userId }) {
// Вариант 1: key (красиво)
return <Form key={userId} userId={userId} />;
// Вариант 2: useEffect (старый способ)
// useEffect(() => { setFormData(...) }, [userId]);
}
// При смене userId:
// Form полностью пересоздаётся
// Всё состояние обнуляется
Итоговые правила
- Props меняется → компонент перерендеривается, но переиспользуется
- Key меняется → компонент полностью пересоздаётся с нулевым состоянием
- Используй key для сброса состояния (вместо useEffect'ов)
- НЕ используй index как key в списках (может быть изменяемым)
- Используй уникальные ID как key (user.id, post.id и т.д.)
Запомни:
- Props — это как параметры функции
- Key — это как идентификатор элемента в списке
- Смена props = обновление компонента
- Смена key = полная пересборка компонента