React
useTransition, useDeferredValue
코딩 화이팅
2025. 1. 24. 14:27
- 리액트 18 버전 이후부터 렌더링 성능이 저하되는 컴포넌트에서 쓸 수 있는 혁신적인 기능이 추가됐다.
- useTransition, useDeferredValue로 오래 걸리는 부분을 감싸면 렌더링 시 버벅이지 않게 해준다.
리액트 18버전부터 추가된 기능 1 : 일관된 batching
setCount(1)
setName(2)
setValue(3) //여기서 1번만 재렌더링됨
- automatic batching 이라는 기능
- state 변경함수를 연달아서 3개 사용하면 재렌더링도 원래 3번 되어야 하지만 재렌더링을 마지막에 1회만 처리해주는 기능
- 쓸데 없는 재렌더링 방지 기능이고 batching이라고 한다.
fetch().then(() => {
setCount(1) //재렌더링됨
setName(2) //재렌더링됨
})
- 리액트 17버전까진 ajax 요청, setTimeout 안에 state 변경함수가 있는 경우 batching이 일어나지 않는다.
- 18버전 이후부터는 어디 있든 간에 재렌더링은 마지막에 1번만 된다.
- 만약 batching 되는게 싫고 state 변경함수 실행마다 렌더링시키고 싶다면 flushSync 라는 함수를 쓰면 된다.
리액트 18버전부터 추가된 기능 2 : useTransition
재렌더링이 느린 컴포넌트 만들어보기
import {useState} from 'react'
let a = new Array(10000).fill(0)
function App(){
let [name, setName] = useState('')
return (
<div>
<input onChange={ (e)=>{ setName(e.target.value) }}/>
{
a.map(()=>{
return <div>{name}</div>
})
}
</div>
)
}
- 데이터가 10000개 들어있는 array 자료를 하나 만들고 그 갯수만큼 <div>를 생성
- 유저가 타이핑할 수 있는 <input>도 생성
- 유저가 <input>에 타이핑하면 그 글자를 <div> 1만개 안에 집어넣어줘야되는데 <div> 1만개 렌더링해주느라 <input>도 많은 지연 시간이 발생
- 따라서 렌더링 지연 시간이 발생하게 된다.
useTransition 사용한다면?
import {useState, useTransition} from 'react'
let a = new Array(10000).fill(0)
function App(){
let [name, setName] = useState('')
let [isPending, startTransition] = useTransition()
return (
<div>
<input onChange={ (e)=>{
startTransition(()=>{
setName(e.target.value)
})
}}/>
{
a.map(()=>{
return <div>{name}</div>
})
}
</div>
)
}
- useTransition() 쓰면 그 자리에 [변수, 함수]가 남는다.
- 그 중 우측에 있는 startTransition() 함수로 state 변경함수 같은 걸 묶는다면 그걸 다른 코드들보다 나중에 처리해준다.
- 따라서 <input> 타이핑 같이 즉각 반응해야하는 걸 우선적으로 처리해줄 수 있다.
- 근본적인 성능개선이라기보단 특정 코드의 실행 시점을 뒤로 옮겨주는 것 뿐이긴함.
isPending?
{
isPending ? "로딩중기다리셈" :
a.map(()=>{
return <div>{name}</div>
})
}
- startTransition()으로 감싼 코드가 처리중 일 때 true로 변하는 변수
- useTransition으로 감싼게 처리완료되면 <div>{name}</div> 이게 보여진다.
useDeferredValue
import {useState, useTransition, useDeferredValue} from 'react'
let a = new Array(10000).fill(0)
function App(){
let [name, setName] = useState('')
let state1 = useDeferredValue(name)
return (
<div>
<input onChange={ (e)=>{
setName(e.target.value)
}}/>
{
a.map(()=>{
return <div>{state1}</div>
})
}
</div>
)
}
- startTransition() 이랑 용도는 비슷
- 하지만 state 아니면 변수 하나를 집어넣을 수 있게 되어있다.
따라서 그 변수에 변동사항이 생기면 그걸 늦게 처리해준다. - 위처럼 코딩하면 이전과 똑같은 기능을 개발 가능
- useDeferredValue 안에 state를 집어넣으면 그 state가 변동사항이 생겼을 때 나중에 처리해준다.