[번역] React.useDeferredValue() 는 어떻게 동작하나요?

Ted LeeTed Lee
3 min read

영문 블로그 글을 번역했습니다. 허가를 받으면 시리즈를 이어갈 예정입니다.
원문링크:
https://jser.dev/react/2022/01/26/how-does-react-usedeferredvalue-work


ℹ️React Internals Deep Dive 에피소드 17, 유튜브에서 제가 설명하는 것을 시청해주세요.

React@18.2.0기준, 최신 버전에서는 구현이 변경되었을 수 있습니다.

💬 역자 주석: JSer의 코멘트는 ❗❗로 표시 해뒀습니다.
그 외 주석은 리액트 소스 코드 자체의 주석입니다.
... 은 생략된 코드입니다.

React 동시 모드에서는 useTransition()Suspense 외에 useDeferredValue()라는 API가 하나 더 있는데, 이것이 무엇이고 어떻게 작동하는지 알아보겠습니다.

useDeferredValue() 는 어떤 문제를 해결하려고 하나요?

React 홈페이지에 자세한 답변이 있지만 데모가 깨져 있습니다.

여기서 간단히 시연해 보겠습니다.

useDeferredValue()가 없는 경우

데모는 useDeferredValue()가 없는 경우입니다.

Next 버튼을 클릭하면, 제목과 게시물에 대한 두 개의 API 목(Mock)이 실행되며, 제목 API는 더 빠르고(300ms) 게시물 API는 느리게(1000ms) 실행됩니다. 다음 버튼을 클릭하면 약 1000ms 후에 제목과 글이 모두 전환되는 것을 확인할 수 있습니다.

이는 버튼 클릭 핸들러가 useTransition()을 사용하고 있기 때문인데, 게시글 API가 데이터를 반환할 때까지 에러가 발생해서, 제목과 글이 모두 지연되기 때문입니다.

이것은 좋지 않은데, 제목이 더 빨리 표시되지 않는 이유는 무엇인가요?

useDeferredValue()가 있는 경우

데모는 useDeferredValue()가 있는 경우입니다.

Next 버튼을 다시 한번 클릭하면, 제목이 먼저 보여지고, 게시글이 따라오는 것을 볼 수 있습니다.

이게 훨씬 낫습니다.

useDeferredValue()를 직접 생성해보자

useTransition()의 작동 방식은 이미 다루었습니다. 간단히 말해서 에러가 발생하면 커밋을 중지하는 것입니다.

여기서 우리의 문제는, 제목 API가 데이터를 반환하면 React가 리렌더링을 시도하지만 게시물이 렌더링될 때 데이터가 준비되지 않았기 때문에 에러를 던져서 발생하는 문제입니다.

function ProfilePage({ resource }) {
  return (
    <React.Suspense fallback={<h1>Loading profile...</h1>}>
      <ProfileDetails resource={resource} />
      <React.Suspense fallback={<h1>Loading posts...</h1>}>
        <ProfileTimeline resource={resource} />
      </React.Suspense>
    </React.Suspense>
  );
}

이 문제를 해결하기 위해 게시물 섹션의 리소스를 state별로 캐시하고 useEffect()에서 업데이트할 수 있습니다.

function useDeferredValue(value) {
  const [state, setState] = React.useState(value);
  React.useEffect(() => {
    // since value might be promise which causes suspension
    // we should wrap it with startTransition
    React.startTransition(() => {
      setState(value);
    });
  }, [value]);
  return state;
}

됐습니다. 이제 코드를 우리가 구현한 곳으로 변경합니다.

function ProfilePage({ resource }) {
  const deferredResource = useDeferredValue(resource);
  return (
    <React.Suspense fallback={<h1>Loading profile...</h1>}>
      <ProfileDetails resource={resource} />
      <React.Suspense fallback={<h1>Loading posts...</h1>}>
        <ProfileTimeline resource={deferredResource} />
      </React.Suspense>
    </React.Suspense>
  );
}

열려있는 데모에서 우리가 만든 useDeferredValue() 함수를 사용하면 React.useDeferredValue()와 동일하게 작동합니다.

React.useDeferredValue()는 어떻게 동작 하나요?

debugger를 설정하면, 이전에 해왔던 것처럼 마운트용과 업데이트용 소스 코드를 찾을 수 있습니다.

function mountDeferredValue<T>(value: T): T {
  const [prevValue, setValue] = mountState(value);
  mountEffect(() => {
    const prevTransition = ReactCurrentBatchConfig.transition;
    ReactCurrentBatchConfig.transition = 1;
    try {
      setValue(value);
    } finally {
      ReactCurrentBatchConfig.transition = prevTransition;
    }
  }, [value]);
  return prevValue;
}

function updateDeferredValue<T>(value: T): T {
  const [prevValue, setValue] = updateState(value);
  updateEffect(() => {
    const prevTransition = ReactCurrentBatchConfig.transition;
    ReactCurrentBatchConfig.transition = 1;
    try {
      setValue(value);
    } finally {
      ReactCurrentBatchConfig.transition = prevTransition;
    }
  }, [value]);
  return prevValue;
}

잠깐만요, 기본적으로 우리가 작성한 것과 동일합니다! mountStateupdateStateuseState에 대한 구현일 뿐입니다.

도대체 ReactCurrentBatchConfig.transition이 무엇인지 궁금하실 텐데요, startTransition()의 소스를 보겠습니다. (소스)

export function startTransition(scope: () => void) {
  const prevTransition = ReactCurrentBatchConfig.transition;
  ReactCurrentBatchConfig.transition = 1;
  try {
    scope();
  } finally {
    ReactCurrentBatchConfig.transition = prevTransition;
  }
}

똑같습니다!!! 하지만 ReactCurrentBatchConfig.transition은 도대체 무엇을 하는 것일까요?

이 함수 내부의 업데이트는 트랜지션 lanes에서 예약되어야 하며, 일시 중단 시 커밋되지 않도록 React에 알려주는 내부 구현입니다.

자세한 내용은 제 동영상에서 확인할 수 있습니다.

(원본 게시일: 2022-01-26)

0
Subscribe to my newsletter

Read articles from Ted Lee directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ted Lee
Ted Lee

Software engineer for web tech. Interested in sustainable growth as software engineer.