하나의 트리를 가지고 다른 트리로 변환하기 위한 최소한의 연산 수를 구하는 알고리즘의 복잡도는 O(n^3) 를 갖는다.
리액트에 이 알고리즘을 적용하면 1000개의 엘리먼트를 그리기 위해 1000^3 = 10억번의 비교 연산을 수행해야 한다.
따라서 리액트는 O(n) 복잡도의 휴리스틱 알고리즘을 구현했다.
1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어 낸다.
2. 개발자가 key
prop을 통해 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할 지 표시할 수 있다.
두 개의 트리를 비교할 때 React는 두 엘리먼트의 root 엘리먼트 부터 비교한다.
두 root 엘리먼트 타입이 다르면 React는 이전 트리를 버리고 완전히 새로운 트리를 구축한다. 트리를 버릴 때 이 전 DOM 노드들은 모두 파괴된다. 루트 엘리먼트 아래의 모든 컴포넌트도 언마운트되고 그 state도 사라진다.
컴포넌트가 갱신되면 인스턴스는 동일하게 유지되어 렌더링 간 state 유지, props 갱신한다.
DOM 노드의 자식들을 재귀적으로 처리할 때 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다.
자식의 끝에 엘리먼트를 추가하면 두 트리 사이의 변경은 잘 작동한다.
그러나 리스트 맨 앞에 엘리먼트를 추가하는 경우엔 성능이 좋지 않다.
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
위와 같이 맨 위에 엘리먼트를 추가하는 경우 모든 자식을 변경한다. 이러한 비효율은 문제가 될 수 있다.
자식들이 key
를 가지고 있다면 React는 Key를 통해 기존 트리와 이후 트리의 자식들이 일치하는 지 확인한다.
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
2014 key를 가진 엘리먼트가 새로 추가되었고 2015, 2016 key를 가진 엘리먼트는 이동만 하기 때문에 효율적이게 수행된다.
해당 key는 오로지 형제 사이에서만 유일하면 되고, 전역에서 유일할 필요는 없다.
최후의 수단으로 배열의 인덱스를 key로 사용할 수 있다. 항목들이 재배열되지 않는다면 문제없지만 재배열 되는 경우엔 비효율적으로 동작할 것이다. 또 항목의 순서가 바뀌면 key도 바뀌기 때문에 컴포넌트가 의도하지 않은 방식으로 바뀔 수 있다.
리액트가 말하는 재렌더링은 모든 컴포넌트의 render
를 호출하는 것이지 컴포넌트를 엄마운트 시키고 다시 마운트하는 것은 아니다. 렌더링 전후에 변경된 부분만을 적용할 것이다.