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가 변동사항이 생겼을 때 나중에 처리해준다.