리액트18버전 이후부터
렌더링 성능이 저하되는 컴포넌트에서 쓸 수 있는 혁신적인 기능이 하나 추가되었다고 한다.
"useTransition" 이건데 이걸로 오래걸리는 부분을 감싸면 렌더링시 버벅이지 않게 해준다.
사실은 코드 실행시점만 조절해주는 것임...
❗ automatic batching
Batching이란 React가 더 나은 성능을 위해 여러개의 state 업데이트를 한 번의 리렌더링으로 묶어서 진행하는 것을 말한다.
setCount(1)
setName(2)
setValue(3) //여기서 1번만 재렌더링됨
state변경함수를 연달아서 3개 사용하면 재렌더링도 원래 3번 되어야하지만
리액트는 똑똑하게도 재렌더링을 마지막에 1회만 처리해준다.
일종의 쓸데없는 재렌더링 방지기능이고 batching이라고 한다.
fetch().then(() => {
setCount(1) //재렌더링됨
setName(2) //재렌더링됨
})
근데 문제는 ajax요청, setTimeout안에 state변경함수가 있는 경우 batching이 일어나지 않는다.
리액트 17버전까진 그런 식으로 일관적이지 않게 동작했는데
18버전 이후 부터는 어디 있든 간에 재렌더링은 마지막에 1번만 된다.
batching 되는게 싫고 state변경함수 실행마다 재렌더링시키고 싶으면
flushSync라는 함수를 쓰면 됩니다.
❗ flushSync
정말 간단하게 말하면 async await으로 보면 된다.
비동기적인 코드를 강제로 동기적으로 만들고 react에서 리렌더링을 강제하도록 한다.
아래와 같이 사용한다.
import { flushSync } from 'react-dom'; // Note: react-dom, not react
function handleClick() {
flushSync(() => {
setCounter(c => c + 1);
});
// React has updated the DOM by now
flushSync(() => {
setFlag(f => !f);
});
// React has updated the DOM by now
}
너무 좋다.
❗ useTransition
렌더링시간이 매우 오래걸리는 컴포넌트가 있을 때
버튼 클릭, 타이핑할 때 마다 그 컴포넌트를 렌더링해야한다면 이상하게 버튼클릭, 타이핑 반응 속도도 느려진다.
사람들은 원래 클릭, 타이핑을 했을 때 0.3초 이상 반응이 없으면 불편함을 느끼기 때문에 개선방법을 알아봅시다.
당연히 그 컴포넌트 안의 html 갯수를 줄이면 대부분 해결된다.
근데 그런게 안되면 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"을 사용하면 된다.
❗ startTransition
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> 타이핑같이 즉각 반응해야하는걸 우선적으로 처리해줄 수 있다.
타이핑해보면 아까보다 반응속도가 훨씬 낫다.
물론 근본적인 성능개선이라기보단 특정코드의 실행시점을 뒤로 옮겨주는 것일 뿐이다.
html이 많으면 여러페이지로 쪼개는게 좋다..
❗ isPending
startTransition() 으로 감싼 코드가 처리중일 때 true로 변하는 변수이다.
{
isPending ? "로딩 중" :
a.map(()=>{
return <div>{name}</div>
})
}
그래서 이런 식으로 코드짜는 것도 가능하다.
위의 코드는 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>
)
}
이렇게 쓰면 startTransition()과 똑같은 기능을 개발이 가능하다
- useDeferredValue 안에 state를 집어넣으면 그 state가 변동사항이 생겼을 때 나중에 처리해준다.
그리고 처리결과는 let state에 저장해줍니다.
'자바스크립트 - React.js' 카테고리의 다른 글
[React.js] 리액트에서 자주 쓰는 if문 작성 패턴 5개 (0) | 2022.12.19 |
---|---|
[React.js] 컴포넌트간 편리한 state 공유를 위한 Redux (0) | 2022.12.15 |
[React.js] 성능개선 2 : 재렌더링을 막는 memo, useMemo (0) | 2022.12.05 |
[React.js] 성능개선 1 : lazy import (1) | 2022.11.29 |
[React.js] useEffect에 대하여 (0) | 2022.11.27 |