-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Code split out everything that is not needed until after user interaction #839
Comments
Can you elaborate on how you think we could generically do this? Requiring that a bunch of components be manually injected isn't super viable imo; using env vars wouldn't really apply here; we could specify browser entry points but then that wouldn't help bundle size, just server renders. |
I'm just sorta thinking out loud here, and I'm not super familiar with how this package is architected, so I may be way off. I'm also not really sure what might be best practices for adding code splitting to third-party components out of the box, so maybe there's a better solution. But, looking at SingleDatePicker for example, what if we took everything that was rendered by this function and moved it into its own component that was passed in as children that are conditionally rendered. We could keep all of the props automatically wired up by using clone element. That way, the API for using this component would go from something like this: <SingleDatePicker
phrases={phrases}
onDateChange={this.handleDateChange}
onFocusChange={this.handleFocusChange}
/> to something like this: <SingleDatePicker
phrases={phrases}
onDateChange={this.handleDateChange}
onFocusChange={this.handleFocusChange}
>
<DayPickerSingleDateController />
</SingleDatePicker> That would give consumers control over the import of |
Hmm, that seems reasonable but also a pretty big breaking change. Would moving it into a component, alone, help? You could alias it out in your bundler without requiring an API change. |
This could probably be done without a breaking change actually, if we moved most of e.g. SingleDatePicker into a new file, split it up in the way I describe above, and then change SingleDatePicker to import and compose both of these things and pass down all props. Then, consumers who want to use the split up version would simply stop importing SingleDatePicker, and instead import the two separate pieces and compose them together in their app however they want. |
That seems reasonable; I'm a fan anyways of more things being split out into separate components. |
@ljharb Can you think of good names for these components? |
¯\_(ツ)_/¯ |
On a sidenote, I think moment and its locales are definitely also contributing to the size. I was suprised as I've tried |
I'm just still a bit confused as to how the proposed rearchitecting is all that different from what's happening now (e.g. splitting the SDP into multiple component). The SDP (and DRP as well) is basically just a wire between the inputs and the calendar dropdown. The As for passing in @Magellol for replacing moment with something lighter, I've thought a bit about this (although I'm still not sure what would be the best way to approach the implementation). Essentially, a lot of the moment functionality is abstracted away in these helper methods ( |
@majapw The goal is to allow the imports to be separated out cleanly enough with minimal API changes, so that people can use code-splitting features in their bundler (e.g. webpack) to avoid putting most of the code from this package in their main bundle. In the implementation, everything would be mostly the same, except for renderDayPicker. Instead of importing and rendering I think there are two options here. One would be passing in a reference to the component class as a prop, like this: <SingleDatePicker
phrases={phrases}
onDateChange={this.handleDateChange}
onFocusChange={this.handleFocusChange}
controllerComponent={DayPickerSingleDateController}
/> Then you simply remove the import from SingleDatePicker and render it from the prop in renderDayPicker. Alternatively, you could pass it in as JSX either in a prop or as children (as in my example above): <SingleDatePicker
phrases={phrases}
onDateChange={this.handleDateChange}
onFocusChange={this.handleFocusChange}
>
<DayPickerSingleDateController />
</SingleDatePicker> and then you remove the import and change renderDayPicker to use cloneElement to add all of the props it needs. Either of these would allow consumers to create an async wrapper around the controller component for code splitting purposes, and pass that in instead, e.g. <SingleDatePicker
phrases={phrases}
onDateChange={this.handleDateChange}
onFocusChange={this.handleFocusChange}
controllerComponent={LoadableDayPickerSingleDateController}
/> And the code-split wrapper could look like something this: import Loadable from 'react-loadable';
import Loading from './my-loading-component';
export default Loadable({
loader: () => import('react-dates/path/to/DayPickerSingleDateController'),
loading: Loading,
}); Does this help make sense? |
The way we are using react-dates at Airbnb, we often see it appear in our bundles with a lot of added filesize. One recent bundle size analysis I did clocks react-dates in at ~118 KiB!
Although some of the recent work we've done (e.g. #791) should help a little, I think there are more opportunities to improve this aspect of performance for this package.
Since most of these components are never needed for the initial render, I would really like to be able to either easily compose parts of react-dates so that I can code split out anything that won't be visible until after a user interaction, or be able to pull in a version of react-dates that has this code splitting built in.
I think if we are able to land something like this, we could dramatically reduce the bundle size impact of react-dates, which would be a really great win for everyone!
This is spiritually similar to #286, but approached from the bundle size side of things.
The text was updated successfully, but these errors were encountered: