본문 바로가기
React

[React] 성능 개선 2. useTransition, useDeferredValue

by dong_su 2023. 12. 22.

 

리액트 18버전 이후부터 렌더링 성능이 저하되는 컴포넌트에서 쓸 수 있는 기능들이 추가 되었다.

 

18버전 이후 추가된 기능 1 : 일관된 batching

setCount(1) 
setName(2) 
setValue(3)   //여기서 1번만 재렌더링됨

 

  • state 변경함수를 연달아 사용하면 재렌더링도 원래 3번 되어야 하지만 마지막에 1회만 처리해준다.
  • 일종의 불필요한 재렌더링 방지기능이고 batching이라고 한다.
fetch().then(() => {
    setCount(1)   //재렌더링됨
    setName(2)   //재렌더링됨
})

 

  • 리액트 17버전까지는 ajax요청, setTimeout안에 state 변경함수가 있는 경우 batching이 일어나지 않았는데,
  • 18버전 이후 부터는 어디 있든 간에 재렌더링은 마지막에 1번만 된다.
  • batching 되는게 싫고 state 변경마다 재렌더링 시키고 싶으면 flushSync라는 함수를 쓰면 된다.

18버전 이후 추가된 기능 2 : useTransition 추가

렌더링시간이 매우 오래 걸리는 컴포넌트가 있다고 할 때 버튼 클릭, 타이핑 때마다 렌더링이 된다면

버튼 클릭, 타이핑 반응속도도 느려진다. 이런 경우 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개의 0으로 이루어진 array 자료형 변수 a를 만든다.
  • input에 onChange 이벤트가 일어날 때마다 name 값이 변경되고 재렌더링이 일어난다.
  • <div>{name}</div> 부분이 변수 a의 개수(10000) 만큼 반복되기 때문에 많은 지연시간이 발생한다.

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>
  )
}

 

  • 우측에 있는 startTransition() 함수로 state변경함수 같은걸 묶으면 다른 코드들보다 나중에 처리해준다.
  • 그래서 <input> 타이핑같이 즉각 반응해야 하는 걸 우선적으로 처리해 줄 수 있다.
  • 근본적인 성능개선이라기보단 특정코드의 실행시점을 늦춰주는 기능이다.
{
  isPending ? "로딩중" :  a.map(() => { return <div>{name}</div> })
}

 

  • 좌측에 있는 isPending은 startTransition()으로 감싼 코드가 처리중일 때 true 값을 가지는 변수다.
  • 위 코드는 useTransition으로 감싼 코드의 처리가 완료되면 <div>{name}</div>가 보일것이다.

useDeferredValue

startTransition과 용도는 같다. 근데 이 함수는 state나 변수를 인자로 넣어야 한다.

그래서 넣은 인자에 변동사항이 생기면 실행시점을 늦춰주는 기능이다.

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>
  )
}

 

  • useDeferredValue(state)를 하면 그 state가 변동사항이 생겼을 때 나중에 처리해주고 
  • 처리 결과는 새로운 변수에 할당해서 그 변수를 이용하면 된다.