리액트 18버전 이후부터 렌더링 성능이 저하되는 컴포넌트에서 쓸 수 있는 혁신적인 기능이 하나 추가되었습니다.
useTransition 이건데 이걸로 오래걸리는 부분을 감싸면 렌더링시 버벅이지 않게 해줍니다.(코드실행시점만조절해주는식)
batching
automatic batching이라는 기능이 있는데
state변경함수를 연달아서 3개 사용하면 재렌더링도 원래 3번 되어야하지만
리액트는 재렌더링을 마지막에 1회만 처리해줍니다. 일종의 쓸데없는 재렌더링 방지기능이고 batching이라고 합니다.
setCount(1)
setName(2)
setValue(3) //여기서 1번만 재렌더링됨
근데 문제는 ajax요청, setTimeout안에 state변경함수가 있는 경우 batching이 일어나지 않습니다.
리액트 17버전까진 그런식으로 일관적이지 않게 동작했는데
18버전 이후부터는 어디있든간에 재렌더링은 마지막에 1번만 됩니다.
fetch().then(() => {
setCount(1) //재렌더링됨
setName(2) //재렌더링됨
})
batching 되는게 싫고 state 변경함수 실행마다 재렌더링시키고 싶으면
flushSync라는 함수를 쓰면 됩니다.
useTransition
렌더링시간이 매우 오래걸리는 컴포넌트가 있다고 합시다.
버튼클릭, 타이핑할 때마다 그 컴포넌트를 렌더링해야한다면
이상하게 버튼클릭, 타이핑 반응속도도 느려집니다.
개선하려면 컴포넌트 안의 html갯수를 줄이면 대부분 해결됩니다. 근데 그런게 안되면 useTranstion기능을 쓰면 됩니다.
재렌더링이 느린 컴포넌트
- 데이터가 10000개 들어있는 array자료를 하나 만들고
- 그 갯수만큼 <div>를 생성
- 유저가 타이핑할 수 있는 <input>생성
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>
)
}
유저가 <input>에 타이핑하면 그 글자를 <div> 1만개안에 집어넣어줘야하는데
<div> 1만개 렌더링해주느라 <input>도 많은 지연시간이 발생합니다.
타이핑한 결과도 바로바로 반응이 안옵니다.
여기서 useTranstion을 쓰면
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 변경함수 같은걸 묶으면 그걸 다른 코드들보다 나중에 처리해줍니다.
타이핑을 해보면 아까보다 반응속도가 훨씬 낫지만, 근본적인 성능개선이라기보단 특정코드의 실행시점을 뒤로 옮겨주는 것일 뿐입니다. (html이 많으면 여러페이지로 쪼개자!)
isPending은?
- startTransition() 으로 감싼 코드가 처리중일 때 true로 변하는 변수입니다.
그래서 아래처럼 코드를 짜는것이 가능합니다. useTransiton으로 감싼게 처리완료되면 <div>가 보입니다.
{
isPending ? "로딩중입니다" :
a.map(()=>{
return <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가 변동사항이 생겼을 때 나중에 처리해줍니다. 그리고 처리결과는 let state1에 저장해줍니다.
'프론트엔드 > react' 카테고리의 다른 글
Rest API , 리액트에서 REST API 구축 (1) | 2023.09.04 |
---|---|
react - state 변경함수 사용할 때 주의점 : async (0) | 2023.08.07 |
React - 성능개선(memo, useMemo) (0) | 2023.08.07 |
React - 성능개선(개발자도구 & lazy import) (0) | 2023.08.07 |
React - react-query (0) | 2023.08.07 |