I don't know if it is a bug or I am missing something, but I have a pretty big react/redux application, and simply using the single date picker wrapper component from your examples.
The picker is nested pretty deeply in the Application tree (9/10 level of nesting) and I when I open it and hover with the mouse on the days my entire app became unusable. Here a screenshot from my timeline:

As you can see every mouseout event it triggers a lot of work. Initially I though I was mistakenly attaching a state update to some event, so I stripped out everything and left only your original single date picker wrapper component. To be sure that no state was changing I monitored the root component of my application and the other components that listen the "redux" store and I am sure that none of them is rendering or receiving new props. They do no update.
At this point I am clueless, any suggestion or advice? Can be a bug?
UPDATE: I used the react dev-tools to highlight the components that are updating, and it is clear that only the component is updating.... so how can be possible that exactly the same component of your example lags so much in the context of my application?
Huh, this is really weird. There are def some performance improvements that I have been working on, but it's strange to me that nesting the component would somehow dramatically exacerbate the issue. I wonder if @lencioni has some thoughts on this. He knows a lot of things on web performance and has been helping me iterate on some improvements.
Hey @ciaoben I had this same problem with my application. My problem was related to the isDayBlocked function. To set the hover state the application checks to see if the day is blocked, in order to do so the application needs to loop through all the blocked days on each hover to check if the current day is supposed to be blocked. This extra looping caused my application scripting time to skyrocket especially when I added in more blocked days.
My advice would be to take advantage of the constant time lookup of an object. Inside the componentDidMount function place an operation that takes all of your blocked days and places them in an object. Then in the isDayBlocked all you need to do is put an if/else statement that returns a boolean if the current hovered date is in the blocked dates object(you may need to format the dates so they match up)
I was actually going to suggest this as an update. It seems that the extra looping can give the isDayBlocked function a time complexity that is less than ideal because with each iteration you need to loop over each date, while my suggestion only loop over the blocked dates once.
I hope this helps. If not you should check out how your code is being minified maybe you are injecting things that don't need to be there which could also slow down your application.
@acp31 thanks for the answer, but it's not the case. My code has nothing more than the example:
class SingleDatePickerWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
focused: false,
date: null,
};
this.onDateChange = this.onDateChange.bind(this);
this.onFocusChange = this.onFocusChange.bind(this);
}
shouldComponentUpdate(nextProps, nextState) {
if (nextState.date === this.state.date &&
nextState.focused === this.state.focused) {
return false;
}
return true;
}
onDateChange(date) {
this.setState({ date });
}
onFocusChange({ focused }) {
this.setState({ focused });
}
render() {
const { focused, date } = this.state;
return (
<SingleDatePicker
id="date_input"
date={date}
focused={focused}
onDateChange={this.onDateChange}
onFocusChange={this.onFocusChange}
/>
);
}
}
export default SingleDatePickerWrapper;
Honestly I think it has to do with the size of your application. When I used this calendar before I minified my code it was around 6mb and I had a mouse out scripting time of ~100ms. After I minified my code to around 1mb my mouse out scripting time dropped to ~30ms. Then after I incorporated the updated I mentioned the scripting time was reduced by another 15ms. Are you using webpack?
yeah, I am on webpack. In development my code is not minified but my app is ~ 3mb... I will try it with the minified version, but honestly I think that these times for simple overing days on calendar without any user defined script running are crazy.
@acp31 Do you mind sharing a full example of this rudimentary "caching" of blocked dates?
And @majapw I think you should _seriously_ consider offering this out of the box, it's absolutely crazy to check each day's status for things like hover events. For example, in my use case, we store blockages as ranges, with a start date and end date. Checking if a date falls within a series of ranges inside a isDayBlocked function ends up being so poor on performance that it is a non-option - it gets called a few thousand times in mere seconds of normal use.
It'd make much more sense to just provide an array of moments to isDayBlocked. Users can modify this array if blocking status changes. Unless I'm missing something here?
Update: my initial idea for caching was to create a lookup object, then loop through my blocked days, and set a property of the lookup object with day._d as true.
So one of these objects would look like:
const blockedDates = {
'Fri Apr 07 2017 12:00:00 GMT+0200 (CEST)': true,
'Mon May 01 2017 12:00:00 GMT+0200 (CEST)': true
}
Then your isDayBlocked function would look like:
determineBlockedStatus = day => {
return blockedDates[day._d] === true
}
But, it turns out this is still a _huge_ hit to performance. Also tried an array via indexOf. Everything I've tried here is unusably slow. Not sure how to proceed, but this seems like a pretty critical issue.
v11.0.0 (I swear the last breaking release for a while) rearchitects modifiers so now there's about the same overhead at mount, but then calendar interactions only updates the relevant CalendarDay objects instead of all of them.
You can see more details in #450, but in the meantime, I'm going to close this issue of being "generally slow". There are still some improvements to be made, notably during the month transition phase, but I think this new architecture opens up a lot of doors. :)
Please let me know if you are still using this library and running into this issue, but i think the above PR should probably help a lot here.
Most helpful comment
Hey @ciaoben I had this same problem with my application. My problem was related to the isDayBlocked function. To set the hover state the application checks to see if the day is blocked, in order to do so the application needs to loop through all the blocked days on each hover to check if the current day is supposed to be blocked. This extra looping caused my application scripting time to skyrocket especially when I added in more blocked days.
My advice would be to take advantage of the constant time lookup of an object. Inside the componentDidMount function place an operation that takes all of your blocked days and places them in an object. Then in the isDayBlocked all you need to do is put an if/else statement that returns a boolean if the current hovered date is in the blocked dates object(you may need to format the dates so they match up)
I was actually going to suggest this as an update. It seems that the extra looping can give the isDayBlocked function a time complexity that is less than ideal because with each iteration you need to loop over each date, while my suggestion only loop over the blocked dates once.
I hope this helps. If not you should check out how your code is being minified maybe you are injecting things that don't need to be there which could also slow down your application.