Skip to main content

3 posts tagged with "Rerendering"

View All Tags

· 3 min read

리액트와 리랜더링 알아보기

바닐라 JS 변경으로 인해 Element를 다시 그린다

const rootElement = document.getElementById('root')

function random() {
const number = Math.floor(Math.random() * (10 - 1) + 1)

const element = `
<button>${number}</button>
`

rootElement.innerHTML = element
}

random()
  • 바닐라 JS에서 요소에 변경이 발생하면 모든 element들이 다시 그려지게 된다

React는 변경된 부분만 다시 그린다

const rootElement = document.getElementById('root')

function random() {
const number = Math.floor(Math.random() * (10 - 1) + 1)

const element = <button>{number}</button>

ReactDOM.render(element, rootElement)
}

random()
  • React에서는 변경된 요소들의 값만 변경이 이루어 진다

모든 요소가 변경되는 것과 특정 요소만 변경되는 것 둘은 장단점이 존재한다

리액트와 리랜더링 알아보기 2

비교 알고리즘

  • 앨리먼트 타입이 바뀌는 경우
    • 이전 앨리먼트는 버리고 새로 그린다
  • 앨리먼트 타입은 같고 props 만 변경된 경우
    • key를 먼저 비교하고, props를 비교해서 변경사항을 반영한다

정리

  • 리액트의 앨리먼트는 불변 객체이다
  • 리액트의 변경 사항 반영은 리액트에게 일임한다
  • 리액트의 비교는 Reconciliation(재조정)이다 비교 연산(알고리즘)을 통해 진행한다
    • 타입이 바뀌면 이전 내용을 버리고 다시 그림
    • 타입이 같으면 key값 비교후 변경된 props에 대한 변경사항만 반영한다
  • Virtual Dom은 리액트가 가상 돔을 가지고 있다가 비교할떄 사용한다

참조

  • 패스트 캠퍼스 온라인 강좌(한 번에 끝내는 React의 모든 것 초격자 패키지 Online)

· 6 min read

동기

  • state나 props가 갱신되면 render() 함수는 새로운 React Element Tree를 반환할 것이다
  • 이때 React는 방금 만들어진 트리에 맞게 가장 효과적으로 UI를 갱신하는 방법을 알아낼 필요가 있다
  • 하나의 트리를 가지고 다른 트리로 변환하기 위한 최소한의 연순 수를 구하는 알고리즘이 필요하다
  • 최첨단의 알고리즘도 n개의 엘리먼트가 있는 트리에 대해 O(n^3)의 복잡도를 가진다(너무 비싼 연산)
  • React는 두가지 가정을 기반하여 O(n) 복잡도의 휴리스틱 알고리즘을 구현했다
  1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다
  2. 개발자가 key prop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다

비교 알고리즘(Diffing Algorithm)

  • 두 개의 트리를 비교할 때, React는 두 엘리먼트의 루트(root) 엘리먼트부터 비교한다
  • 이후의 동작은 루트 엘리먼트의 타입에 따라 달라진다

엘리먼트 타입이 다른 경우

  • 두 엘리먼트의 타입이 다르면, 이전 트리를 버리고 완전히 새로운 트리를 구축한다(새로 그린다는 의미)
  • 에를 들어 a태그에서 img로, Article에서 Comment로 Button에서 div로 바뀌는 케이스가 모두 트리 전체를 재구축 하는 경우이다
  • 트리를 버릴 때 이전 DOM 노드들은 모두 파괴된다
    • compoenentWillUnmount()가 실행된다
  • 새로운 트리가 만들어질 떄, 새로운 DOM 노드들이 DOM에 삽입된다
    • UNSAFE_componentWIllMount()가 실행된다(Depreciated)
    • compoenentDidMount()가 이어서 실행된다
    • 이전 트리와 연관된 모든 state는 사라진다
<div>
<Counter />
</div>

<span>
<Counter />
</span>
  • 엘리먼트 타입이 변경되었기 때문에 Counter는 사라지고, 새로 다시 마운트가 된다

DOM 엘리먼트 타입이 같은 경우

  • 같은 타입의 두 React DOM 엘리먼트를 비교할 때, React는 두 엘리먼트의 속성을 확인하여, 동일한 내역은 유지하고 변겨된 속성들만 갱신한다
<div className='before' title='stuff' />

<div className='after' title='struff' />
  • React는 새로운 엘리먼트의 내용을 반영하기 위해 현재 컴포넌트 인스턴스의 props를 갱신한다
  1. UNSAFE_componentWillReceiveProps
  2. UNSAFE_componentWillUpdate(Depreciated)
  3. componentDidUpdate

자식에 대한 재귀적 처리

  • DOM 노드의 자식들을 재귀적으로 처리할 때, React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다
<ul>
<li>First</li>
<li>Second</li>
</ul>

<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
</ul>
  • First, Second의 일치를 확인하고 Third를 생성한다
  • 비효율이 발생하는 경우
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>

<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
  • React는 Duke 와 Villanova 종속 트리를 그대로 유지하는 대신 모든 자식을 변경한다(비효율)

Keys

  • 위와 같은 순차적 비교에서 발생하는 비효율을 제거하기 위해 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로 설정한다
<li key={item.id}>{item.name}</li>
  • key는 오로지 형제 사이(같은 속성 태그를 의미하는듯)에서만 유일하면 되고, 전역으로는 유일할 필요가 없다
  • 배열의 인덱스를 key로 사용할 수도 있지만, 재배열 하는 경우 비효율적으로 동작할 것이다
  • 인덱스를 key로 사용할 경우 state 관련 문제가 발생할 수 있다
    • 항목의 순서가 바뀔경우 key 또한 바뀌면, 그 결과로 state가 엉망이 되나가 의도치 않은 결과가 생길 수 있다

· 3 min read

엘리먼트 렌더링 React Document 정리

엘리먼트 렌더링

  • 엘리먼트는 React 앱의 가장 작은 단위이다
const element = <h1>Hello, World</h1>
  • 브라우저 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며(plain object) 쉽게 생성할 수 있다
  • ReactDOM은 React 엘리먼트와 일차하도록 DOM을 업데이트 한다

DOM에 엘리먼트 렌더링하기

  • HTML에 root div가 있다고 가정한다
<div id="root"></div>
  • 이 안에 들어가는 모든 엘리먼트를 React DOM에서 관리하기 때문에 이것을 "루트(root)" DOM노드라고 부른다
  • React로 구현된 애플리케이션은 일반적으로 하나의 루트 DOM 노드가 있다
  • React를 기존 앱에 통합하려는 경우 원하는 만큼 많은 수의 독립된 루트 DOM 노드가 있을 수 있다(이해 못함)
  • React 엘리먼트를 렌더링 하기 위해서는 우선 DOM 엘리먼트를 ReactDOM.createRoot()에 전달한다음 React 엘리먼트를 root.render()에 전달해야 한다
const rootElement = document.getElementById('root')
const element = <h1>Hello, World</h1>
ReactDOM.render(element, rootElement)

렌더링 된 엘리먼트 업데이트하기

  • React 엘리먼트를 불변객체이다
  • 엘리먼트를 생상한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다
  • 엘리먼트 영화에서 하나의 프레임과 같이 특정 시점의 UI를 보여준다
  • 위 설명을 바탕으로 하면 UI를 업데이트 하는 유일한 방법은 새로운 엘리먼틑 생성하여 이를 React로 전달하는 것이다
  • Tick Clock Example
const rootElement = document.getElementById('root')

function tick() {
const element = (
<div>
<h1>Hello, World</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
)

ReactDOM.render(element, rootElement)
}

setInterval(tick, 1000)
  • 1초 마다 tick 함수를 호출하여 엘리먼트의 변화를 화면에 보여준다

변경된 부부만 업데이트하기

  • ReactDOM은 해당 엘리먼트와 그 자식 엘리먼트를 이전의 엘리먼트와 비교하고 DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트한다
  • 코드 상에서는 초당 element를 모두 다시 render하지만, 실제 React는 변경된 텍스트 노드만 업데이트 한다