React
는render
될 때마다 특정 알고리즘을 이용해DOM
트리를 비교해서 반영하는걸로 보인다.
그 비교 알고리즘을 공식 문서를 통해 정리해 보려고 한다.
예제 코드의 대부분은 공식 문서에서 가져왔다.
두 개의 트리를 비교할 때, React는 두 엘리먼트의 루트(root) 엘리먼트부터 비교합니다. 이후의 동작은 루트 엘리먼트의 타입에 따라 달라집니다.
이렇게 적혀있는데 두 개의 트리는 아마Real DOM
과Virtual DOM
을 말하는거라고 생각함.
두 개의 엘리먼트 타입이 다를 경우 이전에 있던 트리를 모두 새로운 트리로 바꾼다.
<div> <p>변경 전</p> <div>
를 아래와 같이 바꾼다면
<span> <p>변경 후</p> <span>
div
를span
으로 바꾸고p
의text
를 변경하는게 아니라 기존의div
하위 트리가 모두UnMount
되고span
과 하위 트리가 다시Mount
되는 방식이다.
LifeCycle
은 트리가 삭제되면서componentWillUnmount()
가 실행되고 새로운 트리가 만들어질 때componentWillMount()
가 실행되고componentDidMount()
가 실행된다.
두 엘리먼트의 속성만 비교하여 다른 부분만 변경한다.
<div className='before' style={{color: 'red', fontWeight: 'bold'}} ></div>
<div className='after' style={{color: 'green', fontWeight: 'bold'}} ></div>
위와 같은 경우 변경 된
className
과style
속성 안의color
만 변경되서 적용된다.
state
는 그대로 유지하고,props
는 갱신한다.
LifeCycle
은componentWillReceiveProps()
와componentWillUpdate()
가 실행된다.
DOM 노드의 자식들을 재귀적으로 처리할 때, React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성합니다.
<ul> <li>first</li> <li>second</li> </ul>
<ul> <li>first</li> <li>second</li> <li>third</li> </ul>
변경 전 트리의 마지막에 자식 요소를 추가하면 자식요소를 처음부터 비교하면서 마지막에 추가된 것만 변경됐다고 인식한다.
<ul> <li>Duke</li> <li>Villanova</li> </ul>
<ul> <li>Connecticut</li> <li>Duke</li> <li>Villanova</li> </ul>
그러나 위와 같이 마지막이 아닌 위치에 자식 요소가 추가되면 차이가 발생한 요소부터 트리를 지우고 다시 추가하는 작업을 하는걸로 보인다.
요소들에
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>
key
값이 있다면 위치와 상관없이key
를 비교해서 변경 사항을 인식하기 때문에key
가 없을 때와는 달리 마지막 요소에 추가되는게 아니라도 효율적인 작업을 할 수 있다.
key
는 형제 요소 사이에서만 식별할 수 있으면 되기 때문에 전역에서 관리할 필요가 없다.
(중요)
배열의 경우index
를key
로 사용할 수 있는데 이럴 경우는 요소의 변경이 없을 경우에는 가능한 방법이다.
요소의 위치가 바뀔 때 문제가 발생할 수 있다.
EX)['개', '고양이', '곰']
의index
를 사용할 때<div key='0'>개</div> <div key='1'>고양이</div> <div key='2'>곰</div>
이런 식으로 될 텐데 요소의 위치가 바뀌어서
['고양이', '곰', '개']
가 된다고 하면<div key='0'>고양이</div> <div key='1'>곰</div> <div key='2'>개</div>
그 요소가 가지고 있던 고유
key
값이 계속 바뀔수가 있어서state
관리에 문제가 생길 수 있다.
1. 알고리즘은 다른 컴포넌트 타입을 갖는 종속 트리들의 일치 여부를 확인하지 않습니다. 만약 매우 비슷한 결과물을 출력하는 두 컴포넌트를 교체하고 있다면, 그 둘을 같은 타입으로 만드는 것이 더 나을 수도 있습니다. 우리는 실제 사용 사례에서 이 가정이 문제가 되는 경우를 발견하지 못했습니다.
- 내용 자체가 확 와닿지 않는데 통채로
UnMount
시키는 상황을 만들지 말라는거 같다.
2. key는 반드시 변하지 않고, 예상 가능하며, 유일해야 합니다. 변하는 key(Math.random()으로 생성된 값 등)를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있습니다.
key
를 사용할 때 확실히 사용가능한 값을 써야된다는 얘기 같다.