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에 추가하게 되었습니다.
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>
)
}
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>
)
}
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>
)
}
defaultOptions
→ Suspensive
의 defaultProps
로 이름 변경
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>
)
}