이 포스트는 이 영상을 기반으로 작성한 글입니다.
렌더링은 반드시 순수해야하고 부수효과를 가지면 안됩니다.
그 예는 다음과 같습니다.
Render logic must not
- 기존 변수와 객체를 변경하면 안된다.
Render logic may
- 렌더링중 새로 생성된 객체를 변경
- 에러 던지기
- 캐시된 값과 같이 아직 생성되지 않은 데이터에 대한 "지연 초기화"
리액트 18버전부터는 automatic batching을 모든 시간에 적용했기 때문에
18 미만 버전에서는 렌더링 batching과 timing이 다르다.
const [counter, setCounter] = useState(0);
const onClick = async () => {
setCounter(0);
setCounter(1);
const data = await fetchSomeData();
setCounter(2);
setCounter(3);
}
/*
17버전에서는 총 3번 렌더링
setCounter(0);
setCounter(1);
---------------
setCounter(2);
---------------
setCounter(3);
18버전에서는 총 2번 렌더링
setCounter(0);
setCounter(1);
-----------------
setCounter(2);
setCounter(3);
*/
setState()
는(batch라고 생각하자) 각각 redner pass라는 큐에 호출되지만, 리액트는 여러 개의 batches를 큐에 업데이트하여 동일한 event loop tick에 combined하여 redner pass에 넣는다. 즉 setState
를 비동기라고 하는 이유가 바로 이것이다. 당연히 이것은 성능 최적화를 위해서 이렇게 한다라고 생각이 된다.
리액트 17버전 이하에서는 batching은 리액트 이벤트 핸들러에 의해서만 완료가 되었기에 총 3번의 렌더링이 일어나겠지만, 18버전에서는 automatic batching이 적용되어 await
이후에 setCounter
를 일괄처리하여 rendering한다.
리액트 17 및 이전 버전에서 리액트는 onClick 콜백과 같은 리액트 이벤트 핸들러에서만 일괄 처리를 수행했습니다. setTimeout, await 이후 또는 일반 JS 이벤트 핸들러와 같은 리액트 이벤트 핸들러 외에서의 업데이트는 큐에 추가되지 않았으며 각각 별도의 리렌더링이 발생했습니다.
참고사이트