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}</>
},
}
Now Delay
’s children render prop can use the isDelayed flag. #1312 
The Delay
component now allows you to use the isDelayed
flag in the render prop of its children. This flag can be used to check if the delay has ended.
When we use Skeleton UI for Suspense’s fallback, it is better to give a delay of about 200ms and make the skeleton fade-in rather than showing it right away.
At this time we can use the isDelayed
flag to control the opacity of the skeleton. This allows us to reduce layout shifts while improving user experience.
import { Delay } from '@suspensive/react'
const Example = () => (
<Suspense
fallback={
<Delay ms={200}>
{({ isDelayed }) => (
<Skeleton
style={{
opacity: isDelayed ? 1 : 0,
transition: 'opacity 0.2s ease-in-out',
}}
/>
)}
</Delay>
}
>
<SuspendingComponent />
</Suspense>
)
Handling BREAKING CHANGES
Remove wrap
& Add with
#1452 
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}</>
- })