-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(react): add experimental wrap to remove unnecessary hocs #270
Conversation
🦋 Changeset detectedLatest commit: 20db7a3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Codecov Report
@@ Coverage Diff @@
## main #270 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 23 24 +1
Lines 806 838 +32
Branches 136 143 +7
=========================================
+ Hits 806 838 +32
|
) => { | ||
const Wrapped = (props: TProps) => ( | ||
<AsyncBoundary {...asyncBoundaryProps}> | ||
<Component {...props} /> | ||
</AsyncBoundary> | ||
) | ||
|
||
if (process.env.NODE_ENV !== 'production') { | ||
const name = Component.displayName || Component.name || 'Component' | ||
Wrapped.displayName = `withAsyncBoundary(${name})` | ||
} | ||
|
||
return Wrapped | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary implementations of all hocs is removed by wrap
function
export default wrap(ErrorBoundaryGroup, { blockOutside: false })( | ||
wrap(ErrorBoundary, { fallback: (props) => <>{props.error.message}</>, onError: logError })( | ||
wrap(Suspense.CSROnly, { fallback: <>loading...</> })(() => { | ||
const errorBoundary = useErrorBoundary() | ||
|
||
return ( | ||
<> | ||
<button onClick={() => errorBoundary.setError(new Error('trigger error by useErrorBoundary().setError'))}> | ||
trigger error by useErrorBoundary().setError | ||
</button> | ||
<UseSuspenseQuery queryKey={['with', 1] as const} queryFn={() => api.delay(200, { percentage: 50 })} /> | ||
</> | ||
) | ||
}) | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can compare before, after of wrap
function
between websites/visualization/src/app/react/experimental/wrap/after/page.tsx
and websites/visualization/src/app/react/experimental/wrap/before/page.tsx
Check it please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I compare it through the network tab? Since the user doesn't write the wrap function himself, do I just need to look at the performance aspect in network tab?
Sure, Could you please suggest how can we compare performance?
however, this change can greatly reduce the file size of the library without BREAKING CHANGE. This is a definite improvement. Additionally, the number of public APIs users have to know can be greatly reduced. I think this is a more important improvement than performance. So I hope that the performance comparison does not delay the merge of this feature. Before merging, it would be better to talk about the interface of wrap (tentative name)
rather than comparing performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, I agree about your opinion
@@ -5,6 +5,8 @@ export { ErrorBoundaryGroup, withErrorBoundaryGroup, useErrorBoundaryGroup } fro | |||
export { AsyncBoundary, withAsyncBoundary } from './AsyncBoundary' | |||
export { Delay, withDelay } from './Delay' | |||
|
|||
export { wrap } from './wrap' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to deprecate all hocs
I agree about this BREAKING CHANGES
but if this function is cover our whole hocs, what about better naming rather than wrap
.
My suggestion is with
It is like jest description, using it
it('should error when ....'
it is readable continually by combining it
+ should
before
// v1
withErrorBoundary(ErrorBoundary, {})(MyComponent)
// v2 wrap case
wrap(ErrorBoundary, {})(MyComponent)
suggestion
with(ErrorBoundary, {})(MyComponent)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered with
before too. but in strict mode of JavaScript, with
is keyword.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
And also, because this wrap
function is a function of a more abstract layer, it is named wrap
, which means it wraps more clearly than with
. Just as connect
in react-redux has the meaning of connecting a store and a component. with
is impossible because it is a keyword, and is there a better naming than wrap
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I don't know about with is keyword in strict mode. wrap is better than I thought another naming. Let's Go !
export default wrap(ErrorBoundaryGroup, { blockOutside: false })( | ||
wrap(ErrorBoundary, { fallback: (props) => <>{props.error.message}</>, onError: logError })( | ||
wrap(Suspense.CSROnly, { fallback: <>loading...</> })(() => { | ||
const errorBoundary = useErrorBoundary() | ||
|
||
return ( | ||
<> | ||
<button onClick={() => errorBoundary.setError(new Error('trigger error by useErrorBoundary().setError'))}> | ||
trigger error by useErrorBoundary().setError | ||
</button> | ||
<UseSuspenseQuery queryKey={['with', 1] as const} queryFn={() => api.delay(200, { percentage: 50 })} /> | ||
</> | ||
) | ||
}) | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you used default parameters often, is there a reason?
|
||
class WrapWithoutCSROnly { | ||
constructor(private wrappers: Wrapper[]) {} | ||
Suspense = (props: PropsWithoutChildren<ComponentProps<typeof Suspense>> = {}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to provide default parameter??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought wrap.Suspense, wrap.Suspense.CSROnly, wrap.Delay don't need props object
const Example = wrap.Suspense(/* no parameter is available*/).on(() => {
return 'success'
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all wrap.XXX have optional props object. optional props object is only for wrap.Suspense, wrap.Suspense.CSROnly, wrap.Delay. wrap.ErrorBoundary, wrap.ErrorBoundaryGroup require props object.
Making component by wrap will run only one time. so I thought unnecessary variable allocation is okay. but if you have idea to improve performance, could you make Pull Request to this branch? it will make all ci(test, type:check, lint, etc.) and place to easy to be review
@okinawaa |
fix #269 # Overview <!-- A clear and concise description of what this pr is about. --> I added `wrap` as experimental feature to remove all repetition of hocs implementation and withXXX funciton's nesting. Library size has been reduced by 12%(2,114 bytes) than before ### AS-IS dist/index.cjs 18,377 bytes <img width="261" alt="image" src="https://github.com/suspensive/react/assets/61593290/c891fd26-2000-4fd9-8a5f-5712c0adce90"> ### TO-BE dist/index.cjs if add wrap and remove hocs 17,120 bytes <img width="261" alt="image" src="https://github.com/suspensive/react/assets/61593290/8df4582f-2420-495c-8649-0adf65651abd"> ## I want to deprecate all hocs In v1, I hope to add the `wrap` function as a public API and keep all hocs marked as deprecated, and in v2, I hope to remove all hocs and provide only the `wrap` function. ### Why want to deprecate all hocs 1. To reduce library size more 2. User don't have to know withSuspense, withSuspense.CSROnly, withErrorBoudnary, withAsyncBoundary, withAsyncBoundary.CSROnly, withErrorBoundaryGroup, withDelay. just have to know wrap and existing components. (number of public apis, 15(with hocs) -> 11(without hocs)) 3. `wrap` function allows us to place related code in a more relevant location. We can also create a function wrapped with wrap to reuse it because calling `wrap` will make hoc. #### withXXX ```tsx export default withErrorBoundary( withSuspense( () => { return 'success' }, { fallback: 'loading...' } // far position with name of withSuspense ), { fallback: () => 'error' } // far position with name of withErrorBoundary ) ``` #### wrap ```tsx export default wrap .ErrorBoundary({ fallback: () => 'error' }) .Suspense({ fallback: 'loading...' }) .on(() => { return 'success' }) ``` ## PR Checklist - [x] I did below actions if need 1. I read the [Contributing Guide](https://github.com/suspensive/react/blob/main/CONTRIBUTING.md) 5. I added documents and tests.
fix #269 # Overview <!-- A clear and concise description of what this pr is about. --> I added `wrap` as experimental feature to remove all repetition of hocs implementation and withXXX funciton's nesting. Library size has been reduced by 12%(2,114 bytes) than before ### AS-IS dist/index.cjs 18,377 bytes <img width="261" alt="image" src="https://github.com/suspensive/react/assets/61593290/c891fd26-2000-4fd9-8a5f-5712c0adce90"> ### TO-BE dist/index.cjs if add wrap and remove hocs 17,120 bytes <img width="261" alt="image" src="https://github.com/suspensive/react/assets/61593290/8df4582f-2420-495c-8649-0adf65651abd"> ## I want to deprecate all hocs In v1, I hope to add the `wrap` function as a public API and keep all hocs marked as deprecated, and in v2, I hope to remove all hocs and provide only the `wrap` function. ### Why want to deprecate all hocs 1. To reduce library size more 2. User don't have to know withSuspense, withSuspense.CSROnly, withErrorBoudnary, withAsyncBoundary, withAsyncBoundary.CSROnly, withErrorBoundaryGroup, withDelay. just have to know wrap and existing components. (number of public apis, 15(with hocs) -> 11(without hocs)) 3. `wrap` function allows us to place related code in a more relevant location. We can also create a function wrapped with wrap to reuse it because calling `wrap` will make hoc. #### withXXX ```tsx export default withErrorBoundary( withSuspense( () => { return 'success' }, { fallback: 'loading...' } // far position with name of withSuspense ), { fallback: () => 'error' } // far position with name of withErrorBoundary ) ``` #### wrap ```tsx export default wrap .ErrorBoundary({ fallback: () => 'error' }) .Suspense({ fallback: 'loading...' }) .on(() => { return 'success' }) ``` ## PR Checklist - [x] I did below actions if need 1. I read the [Contributing Guide](https://github.com/suspensive/react/blob/main/CONTRIBUTING.md) 5. I added documents and tests.
fix #269 # Overview <!-- A clear and concise description of what this pr is about. --> I added `wrap` as experimental feature to remove all repetition of hocs implementation and withXXX funciton's nesting. Library size has been reduced by 12%(2,114 bytes) than before ### AS-IS dist/index.cjs 18,377 bytes <img width="261" alt="image" src="https://github.com/suspensive/react/assets/61593290/c891fd26-2000-4fd9-8a5f-5712c0adce90"> ### TO-BE dist/index.cjs if add wrap and remove hocs 17,120 bytes <img width="261" alt="image" src="https://github.com/suspensive/react/assets/61593290/8df4582f-2420-495c-8649-0adf65651abd"> ## I want to deprecate all hocs In v1, I hope to add the `wrap` function as a public API and keep all hocs marked as deprecated, and in v2, I hope to remove all hocs and provide only the `wrap` function. ### Why want to deprecate all hocs 1. To reduce library size more 2. User don't have to know withSuspense, withSuspense.CSROnly, withErrorBoudnary, withAsyncBoundary, withAsyncBoundary.CSROnly, withErrorBoundaryGroup, withDelay. just have to know wrap and existing components. (number of public apis, 15(with hocs) -> 11(without hocs)) 3. `wrap` function allows us to place related code in a more relevant location. We can also create a function wrapped with wrap to reuse it because calling `wrap` will make hoc. #### withXXX ```tsx export default withErrorBoundary( withSuspense( () => { return 'success' }, { fallback: 'loading...' } // far position with name of withSuspense ), { fallback: () => 'error' } // far position with name of withErrorBoundary ) ``` #### wrap ```tsx export default wrap .ErrorBoundary({ fallback: () => 'error' }) .Suspense({ fallback: 'loading...' }) .on(() => { return 'success' }) ``` ## PR Checklist - [x] I did below actions if need 1. I read the [Contributing Guide](https://github.com/suspensive/react/blob/main/CONTRIBUTING.md) 5. I added documents and tests.
fix #269
Overview
I added
wrap
as experimental feature to remove all repetition of hocs implementation and withXXX funciton's nesting.Library size has been reduced by 12%(2,114 bytes) than before
AS-IS dist/index.cjs
18,377 bytes
TO-BE dist/index.cjs if add wrap and remove hocs
17,120 bytes
I want to deprecate all hocs
In v1, I hope to add the
wrap
function as a public API and keep all hocs marked as deprecated, and in v2, I hope to remove all hocs and provide only thewrap
function.Why want to deprecate all hocs
wrap
function allows us to place related code in a more relevant location. We can also create a function wrapped with wrap to reuse it because callingwrap
will make hoc.withXXX
wrap
PR Checklist