2023년 6월 19일
React
조회 : 307|4분 읽기
리액트의 동시성
리액트의 동시성
리액트 18에서 동시성 도입을 어떻게 구현했는지
리액트 동시성의 기본 전제
다음 뷰(상태가 업데이트된 뷰)를 렌더링하는 동안 현재 뷰의 반응성을 유지하도록 렌더링 프로세스를 재작업하는 것
변화된 state가 급하게 필요하지 않은 경우 업데이트 상황에서 UI 업데이트를 지연시키며 연산 결과를 백그라운드에서 진행해 연산이 완료되면 re-render하며 UI 업데이트
요약
싱글 스레드이기 때문에 state 변화, UI 업데이트, 이벤트 반응에 대한 처리를 한 번에 하지 못하기 때문에
UI 업데이트(state 변화로 인한)가 일어날 때 반응성이 없어지는 일을 처리하기 위해 동시성 개념을 도입
상태 변화를 바로 UI에 적용하지 않고 백그라운드에서 연산이 끝난 후 re-rendering한 후 커밋을 마치고 UI 업데이트 진행함
useTransition
state 변화가 일어날 때 UI를 막지 않고 state를 update 하는 리액트 훅
javascript1const [isPending, startTransition] = useTransition()
- isPending(state를 update 시키는 pending condition - boolean), startTransition(새롭게 할 state를 기록하는 함수)
- 매개변수를 갖지 않는다.
startTransition function
매개변수
setState를 한개 이상 부르는 함수
리액트는 즉시 매개변수가 없는 스코프를 호출하고 모든 동기식으로 예약된 모든 state를 전환으로 표시한다
이 상태들은 non-blocking으로 진행되며 원치 않는 로딩 표시들을 표시하지 않을 수 있다.
반환 값
없음
주의 사항
useTransition은 훅이기 때문에 컴포넌트 or 커스텀 훅 내에서만 사용할 수 있다.
만약 라이브러리내에서 사용하고 싶다면 startTransition 하나만 호출해 사용
set 함수(setState)에 접근할 수 있는 상태들에 대해서만 사용할 수 있다.
만약 커스텀 훅이나 prop에 반응하게 만들 고 싶다면 useDefferredValue 사용 -> 제어 컴포넌트에선 절대 사용하지 않음
startTransition에 넘기는 함수는 동기적이어야한다.
리액트는 즉시 이 함수(transition이 진행되는 동안 모든 상태를 업데이트 기록하는)를 실행시키기 때문에
timeout 같은 비동기 작업을 넣는다면 transition 으로 기록되지 않을 것
transition으로 기록된 상태 update는 다른 상태 update에 의해 중단될 것이다.
-> transition에서 상태를 업데이트 하는 중에 re-render가 일어나면 다시 transition의 업데이트가 일어남
transition을 안정적으로 사용하려면 state나 작업에 따라 컴포넌트 분리해야함
사용
non-blocking transition으로 state update를 기록하기 위해 컴포넌트 내 최상위 레벨에서 호출
set 함수를 startTransition 에서 실행되도록함
javascript1import { useState, useTransition } from 'react'; 2 3function TabContainer() { 4 const [isPending, startTransition] = useTransition(); 5 6function selectTab(nextTab) { 7 startTransition(() => { 8 setTab(nextTab); 9 }); 10 } 11//... 12}
useDeferredValue
UI의 일부의 update를 연기할 수 있는 리액트 훅
javascript1const deferredValue = useDeferredValue(value)
매개변수
value : 연기하고 싶은 값
반환 값
개발자가 제공한 값과 똑같은 연기된 값을 반환한다.
update 동안 리액트는 처음 오래된 값과 함께 re-render를 시도할 것이다.
그리고 백그라운드에서 새로운 값과 다른 re-render를 시도할 것이다. -> 새로운 값을 return 함
주의 사항
useDefferredValue에 전달되는 값들은 원시 타입의 값 들이거나 렌더링 범위의 밖에서 생성된 객체 여야한다.
만약 개발자가 렌더링이 일어나며 새로운 객체를 생성하고 즉시 이 값을 훅에 전달한다면 매 렌더링마다 다른 값을 값을 내놓을 것이고 불필요한 백그라운드 re-render를 발생시킬 것이다.
현재 render 상태(예전 값을 사용하고 있음)에서 Object.is(엄격한 객체 비교)를 사용해 비교해 다른 값을 받게 될 때 새로운 값과 함께 백그라운드에서 re-render를 예약한다.
백그라운드 re-render는 중단 가능성이 존재한다.
만약 value에 다른 update가 존재한다면 리액트는 백그라운드에서 맨처음부터 re-render를 다시 시작할 것이다.
예를 들어 만약 유저의 input 입력이 chart가 연기된 값을 수신해 re-render하는 것보다 빠르다면 chart는 유저가 입력을 중지한 후에만 re-render된다.
이 훅은 UI부분에서 상태 업데이트에 대한 값을 지연해주는 것이지 네트워크 요청에 대한 디바운스와 같은 역할은 해주지 않는다.
백그라운드에서 일어나는 re-render는 커밋이 완료될 때까지 스크린에 영향을 주지 않는다.
만약 백그라운드 re-render가 중단되면 데이터를 load하고 UI update를 한 후 영향을 줄 것이다.
사용
새로운 값이 준비중일 때 오래된 값을 보여준다.
javascript1import { useState, useDeferredValue } from 'react'; 2 3function SearchPage() { 4 const [query, setQuery] = useState(''); 5 const deferredQuery = useDeferredValue(query); 6 // ... 7}
초기 렌더 동안 연기된 값은 개발자가 제공한 값과 똑같은 값이 된다.
업데이트를 하는 동안 deferred value은 최신 값을 지연한다.
리액트는 처음 연기된값을 update하는 것 없이 re-render하고 백그라운드에서 새로운 값을 받으며 re-render를 시도할 것이다.