Frameworks, Platforms and Libraries/React

useTransition와 useDeferredValue 알아보기

iam102 2024. 4. 28. 13:59

React 18버전에서 새로 나온 hooks인 useTransition을 이번에 사용하게 되어 팀 내에서도 공유하고 포스팅도 하게 되었습니다. useTransition와 비슷한 역할을 가진 useDeferredValue도 같이 알아보겠습니다.

 

useTransition?

React 공식 홈페이지에서 useTransition 설명은 다음과 같습니다.

useTransition is a React Hook that lets you update the state without blocking the UI.
useTransition 은 UI를 차단하지 않고 state를 업데이트할 수 있는 React 훅입니다.

 

useTransition은 파라미터를 받지 않고 isPending, startTransition 두 개의 값을 반환합니다. isPending은 트랜지션의 지연 상태를 나타내는 boolean 타입의 flag 값이며 startTransition은 state의 변화를 트랜지션으로 나타내는 함수로 파라미터로 콜백 함수를 받으며 전달받은 함수의 우선순위를 낮춰 지연시키는 함수입니다.

  const [isPending, startTransition] = useTransition();

 

 

해당 내용만 봐선 useTransition의 이해가 쉽게 와닿지 않아 예시를 통해 살펴보겠습니다.

import { ChangeEvent, useEffect, useState } from 'react';

import './App.css';

function App() {
  const [count, setCount] = useState<number>(0);
  const [countArr, setCountArr] = useState<number[]>([]);

  const updateCount = () => {
    const arr = Array(100000)
      .fill(0)
      .map((_, i) => count * i);
    setCountArr(arr);
  };

  useEffect(() => {
    updateCount();
  }, [count]);

  return (
    <>
      <h1>React Hooks Test</h1>
      <div className="section">
        <input
          type="number"
          value={count}
          onChange={(e: ChangeEvent<HTMLInputElement>) =>
            setCount(Number(e.currentTarget.value))
          }
        />

        <div className="box">
          {countArr.map((val, idx) => (
            <span key={idx}>{val}</span>
          ))}
        </div>
      </div>
    </>
  );
}

export default App;

 

실제 개발 시 나오지 않을 코드이지만 state의 극단적인 변화를 보이기 위해 작성된 코드입니다. 해당 코드의 결과는 아래와 같습니다.

 

updateCount의 무거운 로직으로 인해 state의 setting이 늦어지며 이에 따라 UI의 끊김 현상을 살펴볼 수 있습니다.

 

useTransition을 사용하여 위의 코드를 개선해 보겠습니다.

import { ChangeEvent, useEffect, useState, useTransition } from 'react';

import './App.css';

function App() {
  const [count, setCount] = useState<number>(0);
  const [countArr, setCountArr] = useState<number[]>([]);

  const [isPending, startTransition] = useTransition();

  const updateCount = () => {
    startTransition(() => {
      const arr = Array(100000)
        .fill(0)
        .map((_, i) => count * i);
      setCountArr(arr);
    });
  };

  useEffect(() => {
    updateCount();
  }, [count]);

  return (
    <>
      <h1>React Hooks Test</h1>
      <div className="section">
        <input
          type="number"
          value={count}
          onChange={(e: ChangeEvent<HTMLInputElement>) =>
            setCount(Number(e.currentTarget.value))
          }
        />
        {isPending && <div className="load-box">loading...</div>}
        {!isPending && (
          <div className="box">
            {countArr.map((val, idx) => (
              <span key={idx}>{val}</span>
            ))}
          </div>
        )}
      </div>
    </>
  );
}

export default App;

 

startTransition을 통해 updateCount 실행의 우선순위를 낮추고 이에 따른 트랜지션 상태를 isPending을 통해 판단하여 조건부 렌더링을 걸어두었습니다.

 

결국 useTransition은 startTransition을 통해 함수 실행의 우선순위를 늦출 수 있으며 이에 따른 지연 상태를 isPending으로 나타냅니다. useTransition을 이용한 UI를 사용자에게 제공 시 더 나은 UX를 가지게 합니다.

 

useDeferredValue?

useTransition과 함께 React 18버전에서 나온 useDeferredValue에 대해 알아보겠습니다. 우선 공식 문서에서 설명은 아래와 같습니다.

useDeferredValue is a React Hook that lets you defer updating a part of the UI.
useDeferredValue는 UI 일부의 업데이트를 지연시킬 수 있는 React 훅입니다.

 

useDeferredValue은 파라미터로 모든 타입의 값을 받을 수 있으며 파라미터로 전달받은 값을 그대로 반환하지만 지연된 버전의 값으로 반환합니다.

const deferredValue = useDeferredValue(value)

 

위의 내용만 봐선 useDeferredValue의 이해가 쉽지 않지만 useTransition과 비슷한 내용이 많이 보입니다. useDeferredValue도 예시를 통해 살펴보겠습니다.

import { useDeferredValue, useState } from 'react';

import './App.css';

function App() {
  const [first, setFirst] = useState<number>(0);
  const [second, setSecond] = useState<number>(0);

  const deferredValue = useDeferredValue(second);

  const updateNumber = () => {
    setFirst((f) => f + 1);
    setSecond((s) => s + 1);
  };

  console.log('first', first);
  console.log('second', second);
  console.log('deferredValue', deferredValue);

  return (
    <>
      <h1>React Hooks Test</h1>
      <div className="section">
        <button onClick={() => updateNumber()}>button</button>
      </div>
    </>
  );
}

export default App;

 

useDeferredValue을 사용하여 선언된 deferredValue의 값의 변화가 다른 state의 변화가 끝난 뒤에 발생되었음을 알 수 있습니다.

 

결론

useTransition과 useDeferredValue는 state의 변화에 대한 우선순위를 낮추는 hook이라는 공통점을 가지며 useDeferredValue는 state 값 자체에 우선순위를 낮추지만 useTransition는 state 변화를 일으키는 함수에 우선순위를 낮추는 차이점을 가집니다.

 

마무리

React의 버전이 업데이트될수록 다양한 기능이 나오고 있습니다. React의 버전을 업데이트할 때 새로운 기능을 파악하여 적재적소에 맞게 사용해야겠습니다.


참고 자료

https://react-ko.dev/reference/react/useTransition

https://react-ko.dev/reference/react/useDeferredValue

https://dev.to/sameer1612/react-v18-usetransition-hook-why-3bml