-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(suspensive.org): add migration guide for @suspensive/react v3
- Loading branch information
Showing
2 changed files
with
198 additions
and
4 deletions.
There are no files selected for viewing
101 changes: 99 additions & 2 deletions
101
docs/suspensive.org/src/content/en/docs/react/migrate-to-v3.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,106 @@ | ||
import { Callout } from '@/components' | ||
|
||
# Migrating to v3 | ||
|
||
## What's new | ||
|
||
### Draft | ||
### Now thrown error in `ErrorBoundary` fallback will be passed to parent [#1409](https://github.com/toss/suspensive/pull/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. | ||
|
||
<Callout> | ||
|
||
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](https://github.com/bvaughn/react-error-boundary) where fallbacks are exposed recursively as follows. | ||
1. children is exposed | ||
2. fallback is exposed | ||
3. fallback is exposed | ||
4. fallback is exposed | ||
5. ... recursively exposed fallback is exposed | ||
|
||
</Callout> | ||
|
||
Now, errors thrown from the fallback of ErrorBoundary are caught by the parent ErrorBoundary. Therefore, the behavior is as follows. | ||
|
||
1. children are exposed | ||
2. fallback is exposed | ||
3. This is expected | ||
|
||
```tsx /This is expected/ /fallback is exposed/ /children is exposed/ | ||
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 | ||
|
||
### Draft | ||
### 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. | ||
|
||
1. You don't need to understand the builder pattern used in wrap. | ||
2. Since wrap includes all components internally, the build size increases. In v3, you can import and use only the components you need. | ||
|
||
```diff | ||
+ 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}</> | ||
- }) | ||
``` |
101 changes: 99 additions & 2 deletions
101
docs/suspensive.org/src/content/ko/docs/react/migrate-to-v3.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,106 @@ | ||
import { Callout } from '@/components' | ||
|
||
# v3로 마이그레이션하기 | ||
|
||
## 새로운 기능 | ||
|
||
### Draft | ||
### 이제 `ErrorBoundary` fallback에서 thrown error 발생시 부모로 전달됩니다 [#1409](https://github.com/toss/suspensive/pull/1409) | ||
|
||
fallback의 에러에 의해 fallback을 재귀적으로 노출하는 것은 개발자가 의도한 것이 아니기 때문에 이를 방지하기 위해 v3에서는 fallback에서 throw된 에러를 부모 ErrorBoundary에 의해 잡히도록 변경되었습니다. | ||
따라서 이것은 새로운 멘탈모델이자 BREAKING CHANGE이므로 새 동작방식을 이해하고 사용하시기 바랍니다. | ||
|
||
<Callout> | ||
|
||
AS-IS v2 ErrorBoundary의 fallback에서 throw된 error는 부모 ErrorBoundary에서 처리할 수 없고 스스로 catch하고 폴백을 재귀적으로 노출되었었습니다 | ||
|
||
> 이는 [react-error-boundary](https://github.com/bvaughn/react-error-boundary)에서도 아래와 같이 fallback이 재귀적으로 노출되도록 동작합니다. | ||
1. children is exposed | ||
2. fallback is exposed | ||
3. fallback is exposed | ||
4. fallback is exposed | ||
5. ... 재귀적으로 fallback is exposed을 노출했습니다 | ||
|
||
</Callout> | ||
|
||
`ErrorBoundary`의 fallback에서 throw된 error는 부모 ErrorBoundary에 의해 잡힙니다. 따라서 아래처럼 동작하게 됩니다. | ||
|
||
1. children is exposed | ||
2. fallback is exposed | ||
3. This is expected | ||
|
||
```tsx /This is expected/ /fallback is exposed/ /children is exposed/ | ||
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}</> | ||
}, | ||
} | ||
``` | ||
|
||
## BREAKING CHANGES 처리하기 | ||
|
||
### Draft | ||
### `wrap` 제거 & `with` 추가 | ||
|
||
v3에서 `wrap`을 제거했습니다. wrap의 기능을 대체할 수 있는 `with` 메소드를 각 컴포넌트에 모두 추가해두었습니다. | ||
|
||
1. wrap에서 사용하는 builder 패턴을 이해하지 않아도 됩니다. | ||
2. wrap은 내부적으로 모든 컴포넌트를 포함하고 있기 때문에 빌드 사이즈가 커집니다. v3에서는 필요한 컴포넌트만 import하여 사용할 수 있습니다. | ||
|
||
```diff | ||
+ 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}</> | ||
- }) | ||
``` |