본문 바로가기
React-study/dil

[DIL] useTransition

by 어느새벽 2024. 6. 24.
const [isPending, startTransition] = useTransition()

 

useTransition은 UI 를 차단하지 않고 state를 업데이트할 수 있는 React 훅이다.

 

참조 

컴포넌트의 최상위 레벨에서 useTransition을 호출하여 일부 state 업데이트를 트랜지션으로 표시한다.

import { useTransition } from 'react';

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

 

매개변수 

useTransition은 매개변수를 받지 않는다.

반환값

useTransition은 정확히 두 개의 항목이 있는 배열을 반환한다.

  1. 보류 중인 트랜지션이 있는지 여부를 알려주는 isPending 플래그
  2. state 업데이트를 트랜지션으로 표시할 수 있는 startTransition 함수

startTransition function 

useTransition이 반환하는 startTransition 함수를 사용하면 state 업데이트를 트랜지션으로 표시할 수 있다.

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

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

 

매개변수 

scope : 하나 이상의 set 함수를 호출하여 일부 state를 업데이트하는 함수. 리액트는 매개변수 없이 scope를 즉시 호출하고 scope 함수 호출 중에 동기적으로 예약된 모든 state 업데이트를 트랜지션으로 표시한다. 이는 논블로킹이고, 원치 않는 로딩을 표시하지 않을 것이다.

반환값 

startTransition.은 아무것도 반환하지 않는다.

주의사항 

  • useTransition은 훅이므로 컴포넌트나 커스텀 훅 내부에서만 호출할 수 있다. 다른 곳(예: 데이터 라이브러리)에서 트랜지션을 시작해야 하는 경우, 대신 독립형 startTransition을 호출해야 한다.
  • 해당 state의 set함수에 접근할 수 있는 경우에만 업데이트를 트랜지션으로 감쌀 수 있다. 일부 prop이나 커스텀 훅 값에 대한 응답으로 트랜지션을 시작하려면, 대신 useDeferredValue를 사용해보면 된다.
  • startTransition에 전달하는 함수는 동기식이어야 한다. 리액트는 이 함수를 즉시 실행하여, 실행하는 동안 발생하는 모든 state 업데이트를 트랜지션으로 표시한다. 나중에 더 많은 state 업데이트를 수행하려고 하면(예: 타임아웃), 트랜지션으로 표시되지 않는다.
  • 트랜지션으로 표시된 state업데이트는 다른 state 업데이트에 의해 중단된다. 예를 들어, 트랜지션 내에서 차트 컴포넌트를 업데이트한 다음, 차트가 다시 렌더링되는 도중에 입력을 시작하면 리액트는 입력 업데이트를 처리한 후 차트 컴포넌트에서 렌더링 작업을 다시 시작한다.
  • 트랜지션 업데이트는 텍스트 입력을 제어하는 데 사용할 수 없다.
  • 진행 중인 트랜지션이 여러 개 있는 경우, 리액트는 현재 트랜지션을 함께 일괄 처리한다. 이는 향후 릴리스에서 제거될 가능성이 높은 제한 사항이다.

사용법

state 업데이트를 논블로킹 트랜지션으로 표시하기 

컴포넌트의 최상위 레벨에서 useTransition을 호출하여 state 업데이트를 논블로킹 트랜지션으로 표시해야 한다.

import { useState, useTransition } from 'react';

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

 

useTransition은 정확히 두 개의 항목이 있는 배열을 반환한다.

  1. 보류 중인 트랜지션이 있는지 여부를 알려주는 isPending 플래그를 선택한다.
  2. state 업데이트를  트랜지션으로 표시할 수 있는 startTransition 함수이다.

그런 다음, 다음과 같이 state 업데이트를 트랜지션으로 표시할 수 있다.

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

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

 

트랜지션을 사용하면 느린 디바이스에서도 사용자 인터페이스 업데이트의 반응성을 유지할 수 있다.

 

트랜지션을 사용하면 리렌더링 도중에도 UI가 반응성을 유지한다. 예를 들어, 사용자가 탭을 클릭했다가 마음이 바뀌어 다른 탭을 클릭하면 첫번째 리렌더링이 완료될 때까지 기다릴 필요 없이 다른 탭을 클릭할 수 있다.

트랜지션에서 상위 컴포넌트 업데이트하기

useTransition 호출에서도 부모 컴포넌트의 state를 업데이트할 수 있다. 예를 들어, 이 TabButton 컴포넌트는 onclick 로직을 트랜지션으로 감쌉니다.

export default function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  if (isActive) {
    return <b>{children}</b>
  }
  return (
    <button onClick={() => {
      startTransition(() => {
        onClick();
      });
    }}>
      {children}
    </button>
  );
}

 

부모 컴포넌트가 onClick 이벤트 핸들러 내에서 state를 업데이트하기 때문에 해당 state 업데이트는 트랜지션으로 표시된다. 그렇기 때문에 앞의 예시처럼 'Posts'을 클릭한 다음 바로 'Contact'를 클릭할 수 있다. 선택한 탭을 업데이트하는 것은 트랜지션으로 표시되므로 사용자 상호작용을 차단하지 않는다.

 

트랜지션 중에 ‘보류중’ state 표시하기 

useTransition이 반환하는 isPending boolean 값을 사용하여 트랜지션이 진행 중임을 사용자에게 표시할 수 있다. 예를 들어, 탭 버튼은 특별한 'pending state'를 가질 수 있다.

function TabButton({ children, isActive, onClick }) {
  const [isPending, startTransition] = useTransition();
  // ...
  if (isPending) {
    return <b className="pending">{children}</b>;
  }
  // ...

 

원치 않는 로딩 표시 방지하기 

이 예제에서 PostsTab 컴포넌트는 Suspense가 도입된 데이터 소스를 사용하여 일부 데이터를 가져온다. "Posts"탭을 클릭하면 PostsTab 컴포넌트가 중단되어 가장 가까운 로딩 폴백이 나타난다.

 

Suspense가 도입된 라우터 구축하기 

리액트 프레임워크나 라우터를 구축하는 경우 페이지 네비게이션을 트랜지션으로 표시하는 것이 좋다.

function Router() {
  const [page, setPage] = useState('/');
  const [isPending, startTransition] = useTransition();

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

 

두 가지 이유로 이 방법을 권장한다.

  1. 트랜지션은 중단 가능하므로, 사용자는 다시 렌더링이 완료될 때까지 기다리지 않고 바로 클릭할 수 있다.
  2. 트랜지션은 원치 않는 로딩 표시를 방지하여, 사용자가 네비게이션 시 갑작스럽게 이동하는 것을 방지할 수 있다.

에러 바운더리로 사용자에게 오류 표시하기

startTransition에 전달된 함수가 에러를 발생시키면, 에러 바운더리를 사용하여 사용자에게 에러를 표시할 수 있다. 에러 바운더리를 사용하면 useTransition을 호출하는 컴포넌트를 에러 바운더리로 감싸면 된다.

startTransition에 전달된 함수가 에러를 발생시키면 에러 바운더리에 대한 폴백이 표시된다.

import { useTransition } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function AddCommentContainer() {
  return (
    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>
      <AddCommentButton />
    </ErrorBoundary>
  );
}

function addComment(comment) {
  // For demonstration purposes to show Error Boundary
  if (comment == null) {
    throw new Error("Example Error: An error thrown to trigger error boundary");
  }
}

function AddCommentButton() {
  const [pending, startTransition] = useTransition();

  return (
    <button
      disabled={pending}
      onClick={() => {
        startTransition(() => {
          // Intentionally not passing a comment
          // so error gets thrown
          addComment();
        });
      }}
    >
      Add comment
    </button>
  );
}

 

'React-study > dil' 카테고리의 다른 글

[모던리액트 Deep Dive] 1장 리액트 개발을 위해 꼭 알아야 할 자바스크립트  (7) 2024.11.05
[DIL] useDeferredValue  (0) 2024.06.24
[DIL] useCallback  (0) 2024.06.18
[DIL] useMemo  (0) 2024.06.17
[DIL] useLayoutEffect  (0) 2024.06.11