frontend

useTransition / useDefferedValue 스터디 기록용

하리하링웹 2024. 2. 18. 16:58

useTransition

useTransition은 React 18버전에서 추가된 동시성을 다를 수 있는 새로운 훅입니다.

React의 모든 렌더링은 동기적으로 작동하여 오래걸리는 렌더링 작업이 있으면 유저에게 안좋은 경험을 줄 수 있었지만 useTransition같은 훅을 사용하면 오래걸리는 렌더링 작업에 대해 로딩 화면을 출력하거나 지금 진행중인 렌더링을 버리고 다른 렌더링 과정을 실행하게하는 등 이를 잘 응용한다면 유저 경험을 향상시킬 수 있도록 만들어주는 역할을 할 수 있습니다.

[사용법]

import { useTransition } from 'react';

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  // ...
}

Parameter

-

Returns

  1. isPending: 상태 업데이트가 진행중인지를 확인할 수 있는 boolean 값
  2. startTransition: 중요하지 않은 업데이트 로직을 시작하는데 사용할 수 있는 함수

 

startTransition 함수

  • 상태를 업데이트할 수 있는 한개 이상의 set 함수를 넣어줄 수 있으며 내부에서 일어나는 state의 set은 transition으로 마킹되어 react의 업데이트에서 후순위로 밀려나게 됩니다.
  • startTransition에 넘겨주는 함수는 반드시 동기함수여야하며 setTimeout과 같은 비동기 함수는 제대로 동작하지 않을 수 있습니다.
function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ...
}

[적용 전 후]

ebdf1048-5a13-429e-9697-bd7f55c10fb2.mp4
0.77MB

 

 

 

응용 예시

  • 이벤트 핸들러를 사용해서 부모 컴포넌트 업데이트
export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}
  • isPending을 사용한 로딩 UI
import { useTransition } from 'react';

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}
  • React의 router를 만들 때 transition 사용
    • 라우팅중 사용자가 다른 동작을 하더라도 해당 동작이 정상적으로 동작함
    • 라우팅시 부자연스러운 페이지 이동을 방지할 수 있음
function Router() {
  const [page, setPage] = useState('/');
  const [isPending, startTransition] = useTransition();

  function navigate(url) {
    startTransition(() => {
      setPage(url);
    });
  }
  // ...

혹시 hook을 사용할 수 없는 상황이라면 startTransition을 바로 import해서 사용할수도 있습니다.


useDeferredValue

useDeferredValue는 React 18에서 도입된 또 다른 동시성 모드 훅입니다. 이 훅은 상태 업데이트를 미루고 (defer) 현재 렌더링이 완료된 후에 발생하도록 합니다. 이렇게 하면 오래 걸리는 상태 업데이트로 인해 UI가 끊어지는 현상을 방지할 수 있습니다.

import { useState, useDeferredValue } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  // ...
}

Parameters

  • defer를 원하는 value를 넣어줄 수 있습니다 (any type)

returns

  • 초기 렌더링에서 반환된 값은 동일하지만 업데이트시에 React에서 먼저 처음 제공된 값으로 리렌더링 한 이후 백그라운드에서 새로운 값으로 업데이트해주는 deferred 값을 반환해줍니다.

에를들어 위의 코드 예시에서는 initial render시에 deferred value는 query 값과 동일합니다. 하지만 update시에 최신 query 값보다 지연되어 업데이트하며 이후 백그라운드에서 계산된 새로운 값으로 다시 렌더링을 실행합니다.

 

[사용 예시]

export default function App() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <SearchResults query={deferredQuery} />
      </Suspense>
    </>
  );
}

 

4a1c46d9-c7de-4b82-9552-d7e43acdf55a.mp4
0.48MB

 

이는 마치 debounde와 비슷한 동작을 하는 것처럼 보이나 debounce는 입력받은 지연 시간을 필요로 하지만 deferredValue는 첫 번째 렌더링 이후 자동으로 지연된 렌더링을 수행하므로 더욱 자연스럽게 동작하게 만들 수 있습니다.

 

 

[적용 전 후]

a5bc17c7-5b3b-472d-ac98-17969ae7338f.mp4
0.60MB

 

useDeferredValue가 없는 경우 키를 입력할 때 마다 리스트가 다시 렌더링 되기 때문에 버벅 거리는 동작을 하지만 useDeferredValue를 사용하는 경우에는 리스트의 렌더링을 지연시켜 버벅 거리는 동작을 하지 않는것을 확인할 수 있습니다.


차이점

useTransition은 상태 업데이트 함수를 startTransition으로 마킹하여 React가 우선 순위를 늦추게 하는 반면, useDeferredValue는 state 값 자체만 감싸서 사용하는 차이가 있습니다.

이는 둘 다 동일한 역할을 한다고 말할 수 있으며 상황에 맞게 적절히 사용하면 될 것 같습니다.

예를들어 직접적으로 상태를 업데이트하는 함수를 사용하는 경우 useTransition이 적절하며 props로 state를 받아 사용하는 경우라면 useDeferredValue가 더 적절하다고 볼 수 있습니다.


결론

useTransition과 useDeferredValue는 React 18에서 새롭게 도입된 동시성 모드를 다루는 훅입니다. 이들을 통해 사용자 경험을 향상시키는 데 도움이 될 수 있습니다. 그러나 도입 초기이므로, 이들 훅의 사용에 대해 완전히 이해하고 적용하는 것이 중요합니다.