솔적솔적

React- 성능개선3. useTransition, useDeferredValue 본문

Front-end/REACT - 성능개선 lazy, memo, useTransition

React- 성능개선3. useTransition, useDeferredValue

카드값줘체리 2022. 9. 11. 19:23

리액트가 업데이트되고 나서 사용할 수 있는 신기능이 있다는데, 그것을 오늘 사용해보려한다.

 

| 1. batch 기능

state변경함수들이 쭉 작성이 되어있다면 state변경이 일어날 때마다 재랜더링이 일어나는데

연달아서 여러개가있다면 재랜더링이 맨 마지막것만 재렌더링 1번만 일어나게하는 것이다. 이게 batch이라고 하는데, 

 

예외가 있다. ajax요청, setTimeout 등의 내부코드라면 batch이 일어나지않았는데, 

리액트 17버전에는 배칭이 일어나지않는다.

 

리액트 18버전이후에는 코드가 어디있던간에 잘 일어난다는 것.

 

| 2. useTransition

동작이 느린 컴포넌트들을 빠르게 동작할 수 있게하는 것이다.  

import {useState} from "react";

function App2(){
    const [name, setName] = useState('');

    return (
        <>
            <input onChange={(e) =>{setName(e.target.value)}} />
        </>
    )
}

유저가 input에 뭔가 입력을 하면 name란 state에 저장하도록 하였다.

근데 갑자기 성능저하가 일어났다고 가정을 해보자.

div박스를 천개, 만개정도 만들게되었다치자.

div를 여러개 만드려면 반복문을 사용하겠지.

map을 통해서 div박스들을 반복생성하도록하겠지. 

 

import {useState} from "react";

const divBox = new Array(10000).fill(0);

function App2(){
    const [name, setName] = useState('');

    return (
        <>
            <input onChange={(e) =>{setName(e.target.value)}} />
            {
                a.map(()=>{
                    return <div>{name}</div>
                })
            }
        </>
    )
}

이렇게 코드를 짜고 직접 input란에 입력하여 실행해보면 반응이 느려지는 것을 알 수 있다.

타이핑을 할 때마다 name란 state가 변경이되고 만개정도 생성을 재랜더링해야하기 때문에 느려지는 것이다.

이런 식으로 코드를 짜면 느려지고 문제가 생긴다. 반응이 느린 것을 유저한테 느껴지게한다면 부정적인 효과가 생긴다는 것.

 

여기서 해결책은 무엇이겠는가?

해결방법 1 : 10000개를 지워야지모...

 

해결방법 2 : 꼭 만개를 굳이 보여줘야한다면 리액트 18버전 이후 부터 쓸 수 있는 이 useTransition을 import한다음에 사용하도록하는 것이다.

import {useState, useTransition} from "react";

const divBox = new Array(10000).fill(0);

function App2(){
    const [name, setName] = useState('');

    const [isPending, startTransition] = useTransition();

    return (
        <>
            <input onChange={(e) =>{
                startTransition(()=>{
                    setName(e.target.value)
                })
                
                }} />
            {
                a.map(()=>{
                    return <div>{name}</div>
                })
            }
        </>
    )
}

startTransition으로 해당 state변경 감싸기를 완료한 뒤 

다시 실행해보면 아까보다는 성능이 나아진 것을, 빨리 동작하는 것을 느낄 수 있을 것이다.

 

 

그럼 이 동작원리에 대해서 알아보자.

| startTransition 동작원리

우선, 처음 실행했을 때 왜 늦게 동작을 했었냐면 브라우저는 동시작업을 못하기 때문이다. 

한번에 하나의 작업만 수행할 수 있다는 것(single-threaded) 

그래서 input란에 타이핑을 했을 때 브라우저가 해야할 작업은

input 에 입력된 것을 보여주고 div박스 만개를 동시에 처리하려고하니 느리게 동작되게 되는 것이다.

그래서 startTransition()로 감싸면 이 함수 안에있는 것을 늦게 처리하게해준다.

시작 시점을 뒤로 늦춰준다는 것.

다른 중요한 작업들을 먼저하게끔하는 것.

그럼 브라우저는  중요한 작업 이후에 state감싼 것을 이후에 실행되도록하여 성능향상을 올리는 것이다.

그럴 때 갖다쓰면 좋음좋음

 

| 3. useDeferredValue를 써도 느린 컴포넌트 성능 향상 가능

import {useState, useTransition, useDeferredValue} from "react";

const divBox = new Array(10000).fill(0);

function App2(){
    const [name, setName] = useState('');

    const [isPending, startTransition] = useTransition();

    useDeferredValue();

    return (
        <>
            <input onChange={(e) =>{
                startTransition(()=>{
                    setName(e.target.value)
                })
                
                }} />
            {
                isPending ? '로딩중입니다' : 
                a.map(()=>{
                    return <div>{name}</div>
                })
            }
        </>
    )
}
useDeferredValue(); // 감싸진 않지만 state, props를 넣어서 사용할 수 있다.

이 useDeferredValue()에 넣어둔 state는 늦게 처리가 된다. 변동사항이 생겼을 때 늦게 변화한다는 것. 

늦게 처리되길 원하는 state가 있다하면 변수로 빼서 활용하도록한다.

import {useState, useTransition, useDeferredValue} from "react";

const divBox = new Array(10000).fill(0);

function App2(){
    const [name, setName] = useState('');

    const [isPending, startTransition] = useTransition();

    const state = useDeferredValue(name); // 감싸진 않지만 state, props를 넣어서 사용할 수 있다.

    return (
        <>
            <input onChange={(e) =>{
                startTransition(()=>{
                    setName(e.target.value)
                })
                
                }} />
            {
                isPending ? '로딩중입니다' : 
                a.map(()=>{
                    return <div>{state}</div>
                })
            }
        </>
    )
}

name이라는 state가 변할 때마다 늦게 처리하게끔해줌. 

중요한 처리를 먼저, 그다음 나중에 실행되게함.