React

[React] Suspense 이해하고 활용하기 02 - Promise를 던지는 방법, use 훅

joseph0926 2024. 9. 26. 08:18

[이전 글: Suspense를 활용하면 스피너 지옥에서 탈출할 수 있습니다]

 

[React] Suspense를 활용하면 스피너 지옥에서 탈출할 수 있습니다

[참고: Suspense] – React" data-og-description="The library for web and native user interfaces" data-og-host="react.dev" data-og-source-url="https://react.dev/reference/react/Suspense" data-og-url="https://react.dev/reference/react/Suspense" data-og-ima

joseph0926.tistory.com

 

이전 글에서 `Suspense`에 대해서 알아보고 어떤 방식으로 동작하는지 알아봤습니다.

이번 글에서는 이 `Suspense`에 대해서 좀 더 깊게 알아보고 실제로 어떻게 사용할지에 대해서 작성해보겠습니다.

 

저번 글에서 언급했듯이 `Suspense`는 던져진 `Promise`를 캐치해서 상태르를 파악한다고 서술하였습니다.

그러면 어떻게 던지는걸까요?

 

[참고: Suspense]

 

<Suspense> – React

The library for web and native user interfaces

react.dev

Only Suspense-enabled data sources will activate the Suspense component

 

Suspense를 활성화하려면 Suspense에 대한 처리가 되어있는 컴포넌트에서만 사용 가능하다고 되어있습니다.

그리고 이 예시로 3개를 들고있습니다

1. Data fetching with Suspense-enabled frameworks like Relay and Next.js

2. Lazy-loading component code with lazy

3. Reading the value of a Promise with use

 

1번은 NextJs와 같은 프레임워크 내부에서 자동으로 처리해주는 경우를 뜻합니다.

2번은 lazy 로딩되는 컴포넌트를 뜻합니다

그리고 이번 글에서 집중적으로 다룰 3번의 경우는 `use`훅을 사용하는 경우입니다

(단, `use`훅은 Canary 환경에서만 사용가능합니다)

 

[참고: use]

 

use – React

The library for web and native user interfaces

react.dev

When called with a Promise, the use API integrates with Suspense and error boundaries. The component calling use suspends while the Promise passed to use is pending. If the component that calls use is wrapped in a Suspense boundary, the fallback will be displayed.  Once the Promise is resolved, the Suspense fallback is replaced by the rendered components using the data returned by the use API. If the Promise passed to use is rejected, the fallback of the nearest Error Boundary will be displayed.

 

`use`훅이 어떻게 동작하는지 보기 전에 `use` 훅을 사용한 예제를 먼저 살펴보면 아래와 같습니다

const message = use(messagePromise);

 

이 예제에서 어색한 부분이 있습니다.

일반적으로 javascript에서 비동기를 처리하는 방법이 포함되어있지 않습니다 (콜백, then/catch, async/await)

하지만 message 라는 데이터로 이행되었습니다.

어떻게 된걸까요?

 

결론부터 말하면 Promise에 `.then` 처리 후 던지는겁니다

코드로 `use`훅의 내부를 구현해보겠습니다

function Chat() {
	return (
        <div className="wrapper">
            <Suspense fallback={<div>Loading,,,</div>}>
                <Message />
            </Suspense>
        </div>
	)
}

let message: Message
const messagePromise = getMessage().then((data) => {
	message = data
})

function Message() {
  if (!message) {
    throw messagePromise
  }
  
  return (
    {/* 렌더링 */}
  )
}

 

물론 리액트에서 정의한 `use`훅의 내부는 위의 로직보다 훨씬 복잡하겠지만, 대략적인 느낌은 이러할것입니다.

 

위의 로직을 이해해보자면,

getMessage()라는 Promise를 반환하는 함수에 비동기 처리를 위해 .then 처리를 해줍니다.

 

이후 이 promise 데이터를 사용하는 <Message/> 컴포넌트에서 promise가 아직 이행되지 않았으면 (if (!message) 부분) Promise를 던집니다

이렇게 Promise를 던져서 <Suspense>가 상태를 감지하게 해줍니다

 

이 로직을 `use` 훅을 사용하면 아래처럼 간단해집니다

function Chat() {
  return (
    <div className="wrapper">
      <Suspense fallback={<div>Loading...</div>}>
        <Message />
      </Suspense>
    </div>
  );
}

function Message() {
  const message = use(getMessage());

  return (
    // 렌더링 로직
  );
}