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

В чем разница между перерендером при смене ключа и при смене пропса?

2.0 Middle🔥 172 комментариев
#React#Оптимизация и производительность

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

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

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

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:

  1. Компонент получает новые значения
  2. React вызывает функцию компонента с новыми props
  3. Код внутри функции выполняется заново
  4. Возвращается новый JSX
  5. React сравнивает старый и новый JSX (reconciliation)
  6. 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 полностью пересоздаётся
// Всё состояние обнуляется

Итоговые правила

  1. Props меняется → компонент перерендеривается, но переиспользуется
  2. Key меняется → компонент полностью пересоздаётся с нулевым состоянием
  3. Используй key для сброса состояния (вместо useEffect'ов)
  4. НЕ используй index как key в списках (может быть изменяемым)
  5. Используй уникальные ID как key (user.id, post.id и т.д.)

Запомни:

  • Props — это как параметры функции
  • Key — это как идентификатор элемента в списке
  • Смена props = обновление компонента
  • Смена key = полная пересборка компонента