Migrating to v3
What’s new
Now thrown error in ErrorBoundary
fallback will be passed to parent #1409 
It is not the developer’s intention to expose fallbacks recursively due to fallback errors, in v3, errors thrown from fallbacks are caught by the parent ErrorBoundary. So this is a new mental model and a BREAKING CHANGE, please understand and use the new behavior.
Errors thrown from the fallback of AS-IS v2 ErrorBoundary cannot be handled by the parent ErrorBoundary, so they are caught by themselves and fallbacks are exposed recursively.
This is also the case in react-error-boundary  where fallbacks are exposed recursively as follows.
- children is exposed
- fallback is exposed
- fallback is exposed
- fallback is exposed
- … recursively exposed fallback is exposed
Now, errors thrown from the fallback of ErrorBoundary are caught by the parent ErrorBoundary. Therefore, the behavior is as follows.
- children are exposed
- fallback is exposed
- This is expected
const Example = () => (
<ErrorBoundary fallback={() => <>This is expected</>}>
<ErrorBoundary
fallback={() => (
<Throw.Error message={ERROR_MESSAGE} after={100}>
fallback is exposed
</Throw.Error>
)}
>
<Throw.Error message={ERROR_MESSAGE} after={100}>
children is exposed
</Throw.Error>
</ErrorBoundary>
</ErrorBoundary>
)
const Throw = {
Error: ({
message,
after = 0,
children,
}: PropsWithChildren<{ message: string; after?: number }>) => {
const [isNeedThrow, setIsNeedThrow] = useState(after === 0)
if (isNeedThrow) {
throw new Error(message)
}
useTimeout(() => setIsNeedThrow(true), after)
return <>{children}</>
},
}
Handling BREAKING CHANGES
Remove wrap
& Add with
wrap
has been removed in v3. We added a with
method to each component that can replace the functionality of wrap.
- You don’t need to understand the builder pattern used in wrap.
- Since wrap includes all components internally, the build size increases. In v3, you can import and use only the components you need.
+ import { ErrorBoundaryGroup, ErrorBoundary, Suspense } from '@suspensive/react'
- import { wrap } from '@suspensive/react'
import { useSuspenseQuery } from '@suspensive/react-query'
+ const Example = ErrorBoundaryGroup.with(
+ { blockOutside: false },
+ ErrorBoundary.with(
+ { fallback: ({ error }) => <>{error.message}</>, onError: logger.log },
+ Suspense.with({ fallback: <>loading...</>, clientOnly: true }, () => {
+ const query = useSuspenseQuery({
+ queryKey: ['key'],
+ queryFn: () => api.text(),
+ })
+ return <>{query.data.text}</>
+ })
+ )
+ )
- 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}</>
- })