Оптимизация в React, одна из основных задач, которая заключается в максимально эффективной отрисовке DOM элементов, при их изменении.

React reconciliation (свертка) - алгоритм, используемый для обновления виртуального DOM и определения какие части нужно заменить, а какие оставить без изменения.
Тут, я думаю, стоит начать с того, почему вообще стоит такая задача? Почему бы сразу не перерисовать всё DOM дерево? Дело в том, что работа с DOM деревом является очень трудозатратной операцией и React решает эту проблему с помощью виртуального DOM, которое является легковесной копией реального DOM.
Погружаясь в алгоритмы, можно понять, что базовый алгоритм обработки и рендера DOM элементов составляет O(n^3). Говоря простым языком, то в React для отображения 100 элементов, потребовалось бы около 1 000 000 операций сравнения. Думаю об эффективности такого алгоритма говорить не стоит... НО React решает эту проблему следующим образом.
Команда React решила использовать эвристический алгоритм, сложность которого O(n) и основанного на двух предположениях:
Эвристический алгоритм не имеет как таковой конкретной реализации, это название для любого алгоритма, оптимальность которого не гарантирована, но оно достаточно хорошее для выполнения поставленных задач. Переводя это в область React и рендера DOM элементов, можно сказать что в большинстве случаев React будет модифицировать DOM дерево оптимальным образом, но гарантировать, что для любой отрисовки это будет выполнено максимально эффективно мы не можем (то есть сложность может быть и больше чем O(n)).
Теперь чуть более подробнее о том, как React определяет как ему эффективно отрисовать элементы в DOM.
Для примера, у нас есть следующий JSX-код:
<div class="wrapper">
  <InfoBlock />
</div>Допустим, у нас после некоторых манипуляций тег <div class="wrapper">, в котором у нас находится компонент  <InfoBlock />, превратился в <span class="wrapper">. 
В данном случае это приведет к полной перерисовке тега <div class="wrapper"> (что не удивительно), но и также всех вложенных элементов в него (в нашем случае это компонент <InfoBlock />). 
Теперь рассмотрим другой вариант развития событий
В качестве примера оставим предыдущий:
<div class="wrapper">
  <InfoBlock />
</div>Теперь мы просто хотим добавить новый атрибут id к тегу <div class="wrapper" >, в итоге у нас получится следующее:
<div class="wrapper" id="info-badge">
  <InfoBlock />
</div>Как сейчас поведет себя React?
Наверное хочется сказать, что это приведет так же к полной перерисовке как в прошлом примере... Но React достаточно умный и в данном случае он просто обновить измененный атрибут и все! Такая логика будет справедлива, если мы меняем или добавляем атрибуты в тегах.
Финальный раздел, в котором поговорим о том, как React обрабатывает списки и для чего же нужен это преслову́тый key.
Рассмотрим список <ul>:
<ul class"list">
  <li>Яблоко</li>
  <li>Груша</li>
  <li>Банан</li>
</ul>
Предположим, что мы хотим добавить элемент <li>Апельсин</li>, в конец списка. В данном случае поведение React ожидаемо, он сравнит все элементы начинаю с первого и в конец добавить новый. 
Проблемы начинаются с момента, когда мы пытаемся вставить новый элемент в начало списка или в середину. Для примера рассмотрим случай вставки в начало списка. React опять начинает сравнивать всем элементы друг с другом, и поскольку старый список уже не совпадает с новым, React начинает перерисовывать все элементы списка, как бы сдвигая их вниз.
Такое поведение является неэффективным, так как вместо того чтобы просто вставить новый элемент в начало, React начинает перерисовывать все элементы заново, а как мы уже поняли - любые взаимодействия с DOM являются трудозатратными. Эту проблему и призван решать атрибут key.
Если у дочерних элементов есть ключи, React использует их для сопоставления дочерних элементов в исходном дереве с дочерними элементами в последующем дереве.
Ключи должны быть стабильными, предсказуемыми и уникальными. Нестабильные ключи (например, созданные с помощью Math.random()) приводят к ненужному повторному созданию множества экземпляров компонентов и узлов DOM, что может привести к снижению производительности и потере состояния дочерних компонентов.
Reconciliation в React делится на две основные фазы:
1. Фаза Render Phase (Diffing) — вычисление изменений
Здесь создается новое Work-In-Progress (WIP) Fiber-дерево, которое используется для вычисления изменений.
В этой фазе изменения, вычисленные в предыдущей фазе, применяются к реальному DOM.
По всем вопросам, замечаниям и дополнениям, вы можете написать мне в TG - @ngusev_dev