Skip to content

Commit

Permalink
Add caching to isDayVisible
Browse files Browse the repository at this point in the history
After the optimizations I applied to generating modifiers in
#1659, I noticed that
about 50% of the time in componentWillReceiveProps is spent in
isDayVisible() doing moment operations to generate the boundaries for
the check. Since these boundaries remain very stable across each call,
we can reduce most of the work done here to very little by adding some
caching of these values.

On top of my reduce optimizations, this brings down the time spent in
componentWillReceiveProps to ~15ms.

I'm not doing anything to limit the size of these Maps, so this will
leak some memory, but I think the size of the cache will be really small
in most cases, so I'm not sure it matters.
  • Loading branch information
lencioni committed May 30, 2019
1 parent 5e0c8c1 commit ebbb3f4
Showing 1 changed file with 42 additions and 13 deletions.
55 changes: 42 additions & 13 deletions src/utils/isDayVisible.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,55 @@ import moment from 'moment';

import isBeforeDay from './isBeforeDay';
import isAfterDay from './isAfterDay';
import toISOMonthString from './toISOMonthString';

const startCacheOutsideDays = new Map();
const endCacheOutsideDays = new Map();

const startCacheInsideDays = new Map();
const endCacheInsideDays = new Map();

export default function isDayVisible(day, month, numberOfMonths, enableOutsideDays) {
if (!moment.isMoment(day)) return false;

// Cloning is a little expensive, so we want to do it as little as possible.
// Here we clone the month once and keep mutating that moment object.
const mutableMonth = month.clone();

const firstDayOfFirstMonth = enableOutsideDays
? mutableMonth.startOf('month').startOf('week')
: mutableMonth.startOf('month');
const startKey = toISOMonthString(month);
// eslint-disable-next-line prefer-template
const endKey = startKey + '+' + numberOfMonths;

if (enableOutsideDays) {
if (!startCacheOutsideDays.has(startKey)) {
startCacheOutsideDays.set(startKey, month.clone().startOf('month').startOf('week'));
}

if (isBeforeDay(day, startCacheOutsideDays.get(startKey))) return false;

if (!endCacheOutsideDays.has(endKey)) {
endCacheOutsideDays.set(
endKey,
month.clone().endOf('week').add(numberOfMonths - 1, 'months').endOf('month')
.endOf('week'),
);
}

return !isAfterDay(day, endCacheOutsideDays.get(endKey));
}

// !enableOutsideDays

if (!startCacheInsideDays.has(startKey)) {
startCacheInsideDays.set(startKey, month.clone().startOf('month'));
}

if (isBeforeDay(day, firstDayOfFirstMonth)) return false;
if (isBeforeDay(day, startCacheInsideDays.get(startKey))) return false;

const lastDayOfLastMonth = enableOutsideDays
// We need to call endOf('week') when enableOutsideDays is true, because we
// are reusing the moment object, and our earlier mutation may have moved it
// to a previous month. This should snap us back to a good starting place.
? mutableMonth.endOf('week').add(numberOfMonths - 1, 'months').endOf('month').endOf('week')
: mutableMonth.add(numberOfMonths - 1, 'months').endOf('month');
if (!endCacheInsideDays.has(endKey)) {
endCacheInsideDays.set(
endKey,
month.clone().endOf('week').add(numberOfMonths - 1, 'months').endOf('month'),
);
}

return !isAfterDay(day, lastDayOfLastMonth);
return !isAfterDay(day, endCacheInsideDays.get(endKey));
}

0 comments on commit ebbb3f4

Please sign in to comment.