// userFetcher.ts
let userPromise: Promise<User>;
export function fetchUser(): Promise<User> {
if (!userPromise) {
userPromise = fetch('/api/user').then(res => res.json());
}
return userPromise;
}
// UserInfo.tsx
import { use } from "react";
import { fetchUser } from "./userFetcher";
export default function UserInfo() {
const user = use(fetchUser());
return <div>안녕하세요, {user.name}님</div>;
}
// App.tsx
import { Suspense } from "react";
import UserInfo from "./UserInfo";
export default function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserInfo />
</Suspense>
);
}
use() 훅은 React 18 이상에서만 사용 가능 (18.3.0 이상)use() 훅은 함수 컴포넌트의 최상단에서만 호출 가능 (*일반적인 훅 조건)use() 는 동기적으로 실행되어야 함. await use(...), async function , .then(...) 모두 불가능async function Component() {
const user = use(fetchUser()); // Error
}
function Component() {
const user = await use(fetchUser()); // Error
}
function Component() {
const user = use(fetchUser().then(data => doSomething(data))); // Error
}
use() 가 바로 실행되어야 함.Suspense 는 반드시 use() 보다 상위에 있어야 함.use() 가 던지는 Promise를 트리 위쪽에서 탐색하며 Suspense를 찾음use() 호출 전에 Suspense가 렌더링되고 있어야 함.use() 에 넘기는 값은 같은 Promise 인스턴스여야 함use(promise) 로 받은 Promise를 한 번만 기다림App 컴포넌트 렌더링 → UserInfo 컴포넌트 렌더링 시작use(fetchUser()) 호출userPromise는 아직 undefined 상태fetchUser() 에서 fetch(...) 실행 → 새로운 Promise 생성userPromise 에 생성된 Promise가 할당됨.use() 는 아직 pending 상태인 Promise를 받음 → throw promise 실행fetch(...) 응답 도착 → Promise가 fullfilled 상태로 변경됨UserInfo 를 다시 렌더링use(fetchUser()) 호출userPromise는 이미 존재하고 fullfilled 상태 → fetch가 다시 실행되지 않음use() 는 resolve 값을 받아서 그대로 컴포넌트에서 사용부모 컴포넌트 상태 업데이트 등으로 리렌더링이 발생한다면
UserInfo() 다시 렌더링됨use(fetchUser()) 다시 호출userPromise 는 여전히 메모리에 존재하며 fullfilled 상태fetch() 는 실행되지 않고, 캐시된 Promise의 결과를 그대로 사용React에서 Suspense를 사용할 때, use() 훅을 함께 사용하면 useEffect를 사용하지 않고 비동기 데이터를 간단하게 보여줄 수 있다고 생각합니다. 기존 useEffect 방식은 상태관리와, 로딩 처리, 조건 분기 등을 수동으로 관리해야 했다면, use를 사용하면 렌더링 도중 데이터를 기다리고, 준비되면 UI를 한 번에 렌더링하는 방식이기 때문에 화면 깜박임이 발생하지 않고 사용자에게 한 번에 완성된 UI를 보여줄 수 있다는 장점이 있습니다.