React 공식문서 스터디

useTransition

려낭 2024. 6. 24. 22:17

useTransition이란?

 

React에서 비동기 작업을 처리할 때 유용한 Hook이다.

 주로 사용자 인터페이스(UI)가 즉각적으로 반응하면서도, 시간이 걸리는 작업을 백그라운드에서 처리하고 싶을 때 사용된다. 이 Hook을 사용하면 사용자 경험을 개선할 수 있다. 쉽게 말해, 무거운 작업을 실행하더라도 UI가 매끄럽게 반응하도록 도와준다.

 


 

레퍼런스

 

컴포넌트의 최상위에서 useTransition을 호출하여 일부 state업데이트를 Transition으로 표시.

 

import { useTransition } from 'react';

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

 

 

반환값

 

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

1.isPendin 플래그는 대기중인 Transition이 있는지 알려준다.

2.starTransition 함수는 상태 업데이트를 Transition 으로 표시할 수 있게 해주는 함수이다.

 


startTransition 함수

 

 

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

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

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

 

 

 

매개변수

  • scope는 하나 이상의 setState 함수를 호출하여 일부 상태를 업데이트 하는 함수이다. React는 scope를 매개변수 없이 즉시 호출하며, 이 동안 동기적으로 예약된 모든 상태 업데이트를 Transition으로 표시한다. 이는 비차단(Non-bloking)방식으로 동작하며, 원하지 않는 로딩을 표시하지 않는다.

 

주의사항

 

  • useTransition은 Hook 이므로 컴포넌트나 커스텀 Hook 내부에서만 호출할 수 있다. 다른 곳 (ex.데이터 라이브러리)에서 Transition을 시작해야 하는 경우, 독립형 startTransition을 호출해라.
  • 해당 state의 set 함수에 엑세스 할 수 있는 경우에만 업데이트를 Transition으로 래핑할 수 있다. 일부 prop이나 커스텀 Hook 값에 대한 응답으로 Trasition을 시작하려면 useDeferredValue를 사용해라.
  • startTransition에 전달하는 함수는 동기식이어야 한다. React는 이 함수를 즉시 실행하여 실행하는 동안 발생하는 모든 state 업데이트를 Transition으로 표시한다. 나중에 더 많은 state 업데이트를 수행하려고 하면 (ex.timeout), Transition으로 표시되지 않는다.
  • Transition으로 표시된 state 업데이트는 다른 state 업데이트에 의해 중단된다. 예를 들어, Transition 내에서 차트 컴포넌트를 업데이트 한 다음 차트가 다시 렌더링 되는 도중에 입력을 시작하면 React는 입력 업데이트를 처리한 후 컴포넌트에서 렌더링 작업을 다시 시작한다.
  • 진행중인 Transition이 여러개 있는 경우, React는 현재 Transition을 함께 일괄처리 한다.

 

사용법

 

state 업데이트를 non-blocking Transition으로 표시

 

컴포넌트의 최상위 레벨에서 useTransition을 호출하여 state 업데이트를 non-blocking Transition으로 표시해라.

import { useState, useTransition } from 'react';

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

 

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

 

1.보류중인 Transition이 있는지를 알려주는 isPending 플래그 이다.

2.state 업데이트를 Transition으로 표시할 수 있는 startTransition 함수 이다.

 

그 후 다음과 같이 state 업데이트를 Transition 으로 표시할 수 있다.

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

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

 

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

 

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

 

 


 

Transition에서 상위 컴포넌트 업데이트

 

useTransition 호출에서도 부모 컴포넌트의 state를 업데이트 할 수 있다. 예를 들어, 아래의 TabButton 컴포넌트는 onClick 로직을 Transition으로 래핑한다.

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 업데이트는 Transition으로 표시된다.

그렇기 때문에 앞의 예시에서 처럼 "posts"을 클릭한 다음 바로 "Contact"를 클릭할 수 있다. 선택한 탭을 업데이트 하는 것은 Transition으로 표시되므로 사용자 상호작용을 차단하지 않는다.

 


Transition중에 보류 중인 시각적 state 표시

 

useTransition이 반환하는 isPending boolean 값을 사용하여 transition이 진행중임을 사용자에게 표시할 수 있다. 예를 들어 탭 버튼은 특별한 "pending" 시각적 상태를 가질 수 있다.

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

 


원치 않는 로딩 표시기 방지

 

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

 

import { Suspense, useState } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';

export default function TabContainer() {
  const [tab, setTab] = useState('about');
  return (
    <Suspense fallback={<h1>🌀 Loading...</h1>}>
      <TabButton
        isActive={tab === 'about'}
        onClick={() => setTab('about')}
      >
        About
      </TabButton>
      <TabButton
        isActive={tab === 'posts'}
        onClick={() => setTab('posts')}
      >
        Posts
      </TabButton>
      <TabButton
        isActive={tab === 'contact'}
        onClick={() => setTab('contact')}
      >
        Contact
      </TabButton>
      <hr />
      {tab === 'about' && <AboutTab />}
      {tab === 'posts' && <PostsTab />}
      {tab === 'contact' && <ContactTab />}
    </Suspense>
  );
}

 

로딩 표시기를 표시하기 위해 전체 탭 컨테이너를 숨기면 사용자는 무슨일이 일어나는지 혼란스러울 수 있다. TabButton에 useTransition을 추가하면 탭 버튼에 보류중인 상태를 표시할 수 있다.

 

중요!

Transition은 이미 표시된 콘텐츠(ex.탭 컨체이너)를 숨기지 않을 만큼만 대기한다. 만약posts탭에 중첩된 <Suspense> 경계가 있는 경우 Transition은 이를 대기 하지 않는다.

Suspense-enabled 라우터 구축

 

React 프레임워크나 라우터를 구축하는 경우 페이지 탐색을 Transition으로 표시하는 것이 좋다.

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

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

 

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

  • Transition은 중단할 수 있으므로 사용자는 리렌더링이 완료될 때 까지 기다릴 필요 없이 바로 클릭할 수 있다.
  • Transition은 원치 않는 로딩 표시기를 방지하므로 사용자가 탐색 시 갑작스러운 이동을 방지할 수 있다.
중요!
Suspense-enabled 라우터는 기본적으로 탐색 업데이트를 Transition으로 래핑할 것으로 예상된다.

 


Error boundary로 사용자에게 오류 표시하기

 

startTrasnsition에 전달된 함수가 오류를 발생시키면 error boundary를 사용하여 사용자에게 오류를 표시할 수 있다. error boundary를 사용하려면 useTransition을 호출하는 컴포넌트를 error boundary로 래핑해라. startTransition에 전달된 함수에서 오류가 발생하면, error boundary의 fallback이 표시된다.

 

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 공식문서 스터디' 카테고리의 다른 글

useTransition, useDefferedValue 발표자료  (0) 2024.06.27
useDefferedValue  (0) 2024.06.25
useMemo, useCallback 발표자료  (0) 2024.06.21
useCallback (1)  (0) 2024.06.18
useMemo (2)  (0) 2024.06.18