👀 Suspensive v2에서의 변경을 확인하세요. 더보기 →
문서보기@suspensive/reactv2로 마이그레이션하기

v2로 마이그레이션하기

새로운 기능

새로운 wrap 빌더 #270

컴포넌트를 <Suspense/>, <ErrorBoundary/>, <ErrorBoundaryGroup/>으로 한번에 래핑하는 새로운 기능입니다.

<Suspense/>, <ErrorBoundary/>, <ErrorBoundaryGroup/> 등의 경우 많은 사람들이 hoc를 사용하여 이러한 구성 요소를 구성 요소 주위에 래핑합니다. 이 컴포넌트들은 children에 어떤 처리를 요하기 때문입니다. 그래서 컴포넌트를 불필요하게 나누지 않고 depth를 만들지 않기 위해 각 interface를 위한 hoc인 withErrorBoundary, withErrorBoundaryGroup, withSuspense를 사용하지만 각 hoc를 조합해서 사용하는 경우도 자주 발생하면서 가독성 또한 개선할 필요가 있었습니다. 이를 개선하기 위해 wrap을 제공하기로 했습니다.

import { wrap } from '@suspensive/react'
import { useSuspenseQuery } from '@suspensive/react-query'
 
const Example = wrap
  .ErrorBoundaryGroup({ blockOutside: false })
  .ErrorBoundary({
    fallback: ({ error }) => <>{error.message}</>,
    onError: logger.log,
  })
  .Suspense({ fallback: <>loading...</>, clientOnly: true })
  .on(() => {
    const query = useSuspenseQuery({
      queryKey: ['key'],
      queryFn: () => api.text(),
    })
    return <>{query.data.text}</>
  })

새로운 <ErrorBoundary/>shouldCatch prop #569

Suspensive의 <ErrorBoundary/>는 children에서 발생된 모든 thrown error를 잡아낼 수 있습니다. 하지만 모든 thrown error를 잡아내기 때문에 <ErrorBoundary/>를 사용할 때 더 좁은 위치에 <ErrorBoundary/>를 놓는 것을 고민하게 되었습니다. 이 때문에 어떤 Error를 잡아내야 할지 설정할 수 있는 shouldCatch라는 새 prop을 ErrorBoundary에 추가하게 되었습니다.

ErrorBoundary shouldCatch example

  1. shouldCatch: ErrorConstructor
import { ErrorBoundary } from '@suspensive/react'
 
class CustomError extends Error {}
 
const Example = () => {
  return (
    <ErrorBoundary fallback={({ error }) => <>RestError: {error.message}</>}>
      <ErrorBoundary
        shouldCatch={CustomError}
        onError={logOnCustomError}
        fallback={({ error }) => <>CustomError: {error.message}</>}
      >
        <ThrowErrorComponent />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
  1. shouldCatch: callback
import { ErrorBoundary } from '@suspensive/react'
 
class CustomError extends Error {}
 
const Example = () => {
  return (
    <ErrorBoundary fallback={({ error }) => <>RestError: {error.message}</>}>
      <ErrorBoundary
        shouldCatch={(error) => error instanceof CustomError}
        onError={logOnCustomError}
        fallback={({ error }) => <>CustomError: {error.message}</>}
      >
        <ThrowErrorComponent />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
  1. shouldCatch: boolean
import { ErrorBoundary } from '@suspensive/react'
 
class CustomError extends Error {}
 
const Example = () => {
  return (
    <ErrorBoundary fallback={({ error }) => <>RestError: {error.message}</>}>
      <ErrorBoundary
        shouldCatch={new Date().toISOString() > '2024-01-01T00:00:00.000Z'}
        onError={logOnErrorAfter2024}
        fallback={({ error }) => <>ErrorAfter2024: {error.message}</>}
      >
        <ThrowErrorComponent />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}

새로운 <ErrorBoundary.Consumer/>, <ErrorBoundaryGroup.Consumer/> 컴포넌트 #610

이 컴포넌트는 jsx에서 useErrorBoundary, useErrorBoundaryGroup을 인라인으로 사용할 수 있습니다.

import { ErrorBoundary, ErrorBoundaryGroup } from '@suspensive/react'
 
const Example = () => {
  return (
    <ErrorBoundaryGroup>
      <ErrorBoundaryGroup.Consumer>
        {({ reset }) => <button onClick={reset}>reset all</button>}
      </ErrorBoundaryGroup.Consumer>
      <ErrorBoundary fallback={({ error }) => <>{error.message}</>}>
        <ErrorBoundary.Consumer>
          {({ setError }) => (
            <button onClick={() => setError(new Error('error message'))}>
              setError
            </button>
          )}
        </ErrorBoundary.Consumer>
      </ErrorBoundary>
    </ErrorBoundaryGroup>
  )
}

BREAKING CHANGES 처리하기

<AsyncBoundary/> 제거

v2에서는 <AsyncBoundary/>를 제거했습니다. #295

<AsyncBoundary/>는 내부적으로 <ErrorBoundary/>를 사용하기 때문에 useErrorBoundary와 함께 사용할 수 있으며 <ErrorBoundaryGroup/>의 영향을 받습니다. 우리는 라이브러리 사용자를 위한 유지 관리 및 인터페이스 통합에 더 좋을 것이라고 믿고 이 구성 요소를 v2에서 제거하기로 결정했습니다.

<AsyncBoundary/>의 기능은 두 컴포넌트(<Suspense/>, <Errorboundry/>)를 하나씩 래핑하는 것입니다. 그러면 이렇게 2개로 나눌 수 있습니다.

+ import { Suspense, Errorboundry } from '@suspensive/react'
- import { AsyncBoundary } from '@suspensive/react'
 
+ <Errorboundry fallback={<Error />} onError={onError} onReset={onReset}>
+   <Suspense fallback={<Loading />}>
+     <Children />
+   </Suspense>
+ </Errorboundry>
- <AsyncBoundary pendingFallback={<Loading />} rejectedFallback={<Error />} onError={onError} onReset={onReset}>
-   <Children />
- </AsyncBoundary>

withSuspense, withDelay, withErrorboundry, withErrorBoundaryGroup 제거

이러한 모든 hoc는 v2의 새로운 hoc 빌더 wrap으로 아름답게 대체될 수 있습니다.

+ import { wrap } from '@suspensive/react'
- import { withSuspense, withErrorBoundary, withErrorBoundaryGroup } from '@suspensive/react'
 
+ const Example = wrap
+   .ErrorBoundaryGroup({ blockOutside: false })
+   .ErrorBoundary({ fallback: ({ error }) => <>{error.message}</>, onError: logger.log })
+   .Suspense({ fallback: <>loading...</>, clientOnly: true })
+   .on(() => {
+     const query = useSuspenseQuery({
+       queryKey: ['key'],
+       queryFn: () => api.text(),
+     })
+     return <>{query.data.text}</>
+   })
- const Example = withErrorBoundaryGroup(
-   withErrorBoundary(
-     withSuspense(
-       () => {
-         const query = useSuspenseQuery({
-           queryKey: ['key'],
-           queryFn: () => api.text(),
-         })
-         return <>{query.data.text}</>
-       },
-       { fallback: <>loading...</>, clientOnly: true }
-     ),
-     { fallback: ({ error }) => <>{error.message}</>, onError: logger.log }
-   ),
-   { blockOutside: false }
- )
+ import { wrap } from '@suspensive/react'
- import { withSuspense } from '@suspensive/react'
 
+ const Example = wrap
+   .Suspense({
+     fallback: <>loading...</>,
+     clientOnly: true,
+   })
+   .on(() => {
+     const query = useSuspenseQuery({
+       queryKey: ['key'],
+       queryFn: () => api.text(),
+     })
+     return <>{query.data.text}</>
+   })
- const Example = withSuspense(
-   () => {
-     const query = useSuspenseQuery({
-       queryKey: ['key'],
-       queryFn: () => api.text(),
-     })
-     return <>{query.data.text}</>
-   },
-   {
-     fallback: <>loading...</>,
-     clientOnly: true,
-   }
- )

<ErrorBoundaryGroup.Reset/> 제거

<ErrorBoundaryGroup.Reset/>은 내부적으로 useErrorBoundaryGroup을 사용합니다. 그래서 우리는 이를 Context.Consumer와 같은 것으로 변경하면 React 개발자가 이 컴포넌트의 동작을 더 쉽게 이해할 수 있을 것이라고 생각했습니다. 이름을 <ErrorBoundaryGroup.Consumer/>로 변경하고 인터페이스를 Context.Consumer와 동일하게 유지했습니다.

import { ErrorBoundaryGroup } from '@suspensive/react'
 
const Example = () => {
  return (
    <ErrorBoundaryGroup>
-     <ErrorBoundaryGroup.Reset trigger={(group) => <button onClick={group.reset}>reset all</button>} />
+     <ErrorBoundaryGroup.Consumer>
+       {(group) => <button onClick={group.reset}>reset all</button>}
+     </ErrorBoundaryGroup.Consumer>
    </ErrorBoundaryGroup>
  )
}

defaultOptionsSuspensivedefaultProps로 이름 변경

import { ErrorBoundaryGroup } from '@suspensive/react'
 
const suspensive = new Suspensive({
- defaultOptions: {
+ defaultProps: {
    suspense: {
      fallback: 'default loading...',
    },
  },
})

<Suspense.CSROnly/><Suspense clientOnly/>를 이름 변경(prop으로)

import { Suspense } from '@suspensive/react'
 
const Example = () => {
  return (
-   <Suspense.CSROnly fallback={<>loading...</>}>
+   <Suspense clientOnly fallback={<>loading...</>}>
      <>children</>
    </Suspense.CSROnly>
  )
}