All Declarative APIs ready
<Suspense/>, <ErrorBoundary/>, <ErrorBoundaryGroup/>, etc. are provided. Use them easily without any efforts.
Zero peer dependency, Only React
It is simply extensions of react's concepts. Named friendly with originals like just <Suspense/>, <ErrorBoundary/>, <ErrorBoundaryGroup/>.
Suspense in SSR easily
Suspensive provide clientOnly that make developer can adopt React Suspense gradually in Server-side rendering environment.
1. 대표적인 라이브러리인 TanStack Query로 Suspense 없이 코드를 작성한다면 이렇게 작성합니다.
이 경우 isLoading과 isError를 체크하여 로딩과 에러 상태를 처리하고 타입스크립트적으로 data에서 undefined를 제거할 수 있습니다.
2. 그런데 만약 조회해야 할 api가 더 많아진다고 가정해봅시다.
조회해야 하는 api가 더 많아진다면 이 로딩상태와 에러상태를 처리하는 코드가 더욱 복잡해집니다.
3. Suspense를 사용하면 타입적으로 코드가 간결해집니다. 하지만 컴포넌트의 깊이는 깊어질 수 밖에 없습니다.
useSuspenseQuery는 Suspense와 ErrorBoundary를 사용하여 외부에서 로딩과 에러 상태를 처리할 수 있습니다. 하지만 useSuspenseQuery는 hook이기 때문에 부모에 Suspense와 ErrorBoundary를 두기 위해 컴포넌트가 분리되어야만 하기 때문에 뎁스가 깊어지는 문제가 있습니다.
4. 이 경우 Suspensive에서 제공하는 SuspenseQuery를 사용하면 이렇게 같은 뎁스에서 hook의 제약을 피해 쉽게 코드를 작성할 수 있습니다.
- SuspenseQuery를 사용하면 depth를 제거할 수 있습니다.
- UserInfo라는 컴포넌트를 제거하고 UserProfile과 같은 Presentational 컴포넌트만 남으므로 테스트하기 쉬워집니다.
const Page = () => {const userQuery = useQuery(userQueryOptions())const postsQuery = useQuery({...postsQueryOptions(),select: (posts) => posts.filter(({ isPublic }) => isPublic),})const promotionsQuery = useQuery(promotionsQueryOptions())if (userQuery.isLoading ||postsQuery.isLoading ||promotionsQuery.isLoading) {return 'loading...'}if (userQuery.isError || postsQuery.isError || promotionsQuery.isError) {return 'error'}return (<Fragment><UserProfile {...userQuery.data} />{postsQuery.data.map((post) => (<PostListItem key={post.id} {...post} />))}{promotionsQuery.data.map((promotion) => (<Promotion key={promotion.id} {...promotion} />))}</Fragment>)}
이것이 우리가 Suspensive를 만드는 이유입니다.
더 쉬운 React Suspense를 사용하세요