- Published on
리액트 공식문서 스터디 3-2 주차
- Authors

- Name
- junyeol kim
Render and Commit
Intro
React가 UI를 화면에 띄우는 과정은 세 단계로 이루어진다.
- Triggering : React에
화면을 업데이트 해라라는 요청이 들어온다.- Rendering : 컴포넌트의 함수가 호출되어
JSX 트리를 만든다.- Committing : 결과물을
실제 DOM에 반영한다.
Trigger a render
컴포넌트가 렌더되는 이유는 두 가지가 있다.
- initial render
- state has been updated
initial render
import Image from './Image.js'
import { createRoot } from 'react-dom/client'
const root = createRoot(document.getElementById('root'))
root.render(<Image />)
- 앱이 시작될 때,
createRoot로 타겟 DOM 노드를 지정하고 그 root의render메서드를 사용해 컴포넌트를 불러오는 방식으로 구현된다.
Re-renders
컴포넌트가 한 번 처음 렌더된 이후, state의 set 함수로 업데이트하여 다시 렌더를 트리거할 수 있다.
또한, 컴포넌트의 상태를 업데이트하면 자동으로 대기 목록 ( Queue )에 추가한다.
React renders your components
On initial render : React는 초기 렌더시 root 컴포넌트를 호출한다.
For subsequent renders : 이후의 렌더에서는 상태 업데이트가 발생한 함수형 컴포넌트를 React가 호출한다.
이러한 과정을 재귀적 ( 반복적 )으로 진행되고, 더 이상 중첩된 컴포넌트가 없을 때 까지 반복되어, React는 화면에 무엇을 보여줄지 최종적으로 결정할 수 있게 된다.
초기 렌더 -> 업데이트된 컴포넌트 -> 업데이트된 컴포넌트가 반환한 컴포넌트 -> .. -> 최종 컴포넌트
렌더링은 반드시
pure calculation이어야 한다.토마토만 들어간 샐러드를 주문했는데, 양파가 들어간 샐러드가 나오면 안된다.
React의
Strict Mode는 각 컴포넌트의 함수를 일부러 두 번씩 호출해 함수의 순수성을 검증할 수 있게 해준다.업데이트된 부모 컴포넌트가 tree의 상위에 있다면 모든 중첩 컴포넌트가 리렌더링 되어 성능 측면에서 비효율적 일 수 있다.
성능 최적화에 대한 문서
미리 최적화를 하는 것은 오히려 복잡도만 늘어날 수 있다.
React commits changes to the Dom
React는 컴포넌트들을 렌더링한 이후, 실제 DOM을 수정한다.
initial render : React가 생성한 모든 DOM 노드들을 화면에 표시하기 위해 appendChild() DOM API를 사용한다.
re-renders : React가 렌더링 과정에서 계산한 최소환의 작업만 적용하여 최신 렌더링 결과와 DOM이 일치하도록 만든다.
Browser paint
- Browser Rendering : 렌더링 완료 후 React가 DOM을 업데이트 한 뒤, 브라우저가 화면을 다시 그리는 과정
Recap
𝟭. React 앱에서 화면이 업데이트될 때는 항상 Trigger, Render, Commit 세 단계로 진행된다.
𝟤. Strice Mode를 사용하면 우리가 만든 컴포넌트의 실수를 발견할 수 있다.
𝟥. React는 렌더링 결과가 이전과 같으면, DOM을 건드리지 않는다.
State as a Snapshot
state는 snapshot처럼 동작한다.
값을 set해도 기존 state 변수가 즉시 바뀌는게 아닌 새로운 값으로 re-render된다.
Setting state triggers renders
- 어떤 이벤트에 UI가 반응하려면 반드시 state를 업데이트 해야 한다.
React에서는 클릭 같은 사용자 이벤트에 바로 반응 후 변화라는 직관적인 모델과는 다르게 동작한다.
Rendering takes a snapshot in time
Rendering : React가 컴포넌트 ( 함수 )를 호출하는 것을 의미한다.
함수를 호출한 결과로 나온 컴포넌트는 렌더 시점의 state를 바탕으로 props, event handler, local variables들도 함께 계산된다. -> 그 순간의 UI의 snapshot와 같다.
React re-renders a component :
𝟭. React가 컴포넌트 ( 함수 )를 다시 호출한다.
𝟤. 컴포넌트 ( 함수 )가 새로운 JSX snapshot를 반환하고
𝟥. React가 함수에서 반환된 새로운 snapshot에 맞게 화면을 업데이트 한다.
import { useState } from 'react'
export default function Counter() {
const [number, setNumber] = useState(0)
return (
<>
<h1>{number}</h1>
<button
onClick={() => {
setNumber(number + 1)
setNumber(number + 1)
setNumber(number + 1)
}}
>
+3
</button>
</>
)
}
위 코드에서 +3 버튼을 클릭하게 되면 어떻게 동작할까?
setNumber(number + 1)이 세 번 실행된다. 그렇다면 number값이 3이 되는것인가?
아니다. onClick 이벤트 핸들러가 실행되는 렌더에서는 number의 값이 0으로 동일하다.
그렇기에 세 번 모두 setNumber(1)로 동작하게 되어 number값은 1이다.
왜 이런 상황이 된걸까?
React는 모든 state 변경 요청을 한꺼번에 처리하고 state를 1로 바꾼 뒤, 컴포넌트를 다시 렌더링한다.
이말은 즉, state가 렌더 시점 기준의 snapshot으로 동작하기 때문이다. 그렇기에 setXXX로 여러번 변경을 해도
모두 현재 snapshot 값을 쓴 다음 변경이 한 번만 반영되는 것이다.
Queue a Series of State Updates
React batches state updates :
state를 set하면 즉시 렌더가 일어나는 것이 아니라, 이벤트 핸들러가 모두 끝난 후에 한 번에 처리 ( batching ) 된다.
그래서 setNumber를 여러 번 호출해도 그 이벤트 핸들러가 끝나야 렌더가 발생한다.
Updating the same state multiple times before the next render :
import { useState } from 'react'
export default function Counter() {
const [number, setNumber] = useState(0)
return (
<>
<h1>{number}</h1>
<button
onClick={() => {
setNumber((n) => n + 1)
setNumber((n) => n + 1)
setNumber((n) => n + 1)
}}
>
+3
</button>
</>
)
}
단순히 setNumber(number + 1)을 연속 호출하면, 모두 값은 값(현재 렌더 기준)으로 업데이트 된다.
-> 이말은 즉, 기대값과 결과값이 다르다.
그렇기에 업데이트 함수 방식 ( setNumber(n => n + 1) )을 사용하면 이전 값 기준으로 반영된다.
What happens if you update state after replacing it :
- set 함수를 조합한 여러가지 동작 방식
---------------------------------
setNumber(number + 5)
setNumber(n => n + 1)
→ 최종 결과는 0 + 5 → 5, 5 + 1 → 6
---------------------------------
setNumber(number + 5)
setNumber(n => n + 1)
setNumber(42)
→ 마지막(42)만 남아 state는 42가 됨
---------------------------------
총 정리를 하자면, setState(값)은 교체 요청이고, setState(함수)는 누적 or 연산 요청이다.
또한 업데이터 함수는 항상 이전 state 값을 기준으로 실행된다.
그리고 하나의 이벤트 핸들러 내 여러 setState는 결과적으로 한 번의 렌더로 처리된다.
Naming conventions :
setEnabled(e => !e), setFriendCount(fc => fc + 1), etc.
업데이터 함수의 인자는 상태명을 약어 또는 풀네임으로 쓸 수 있다.
Recap
𝟭. state 설정은 기존 렌더의 변수가 바로 바뀌는게 아니고, 새로운 렌더를 요청하는 것이다.
𝟤. state 업데이트는 이벤트 핸들러가 끝난 후 한 번에 처리 된다( batching ).
𝟥. 여러 번 state를 바꾸려면 업데이터 함수 패턴을 활용하자.