Skip to content

Commit

Permalink
Cache some moment instances when rebuilding modifiers
Browse files Browse the repository at this point in the history
When modifiers need to be rebuilt, which happens pretty frequently, we
iterate over every day and create a new moment instance for these
operations. These moment instances are never mutated, so we can safely
store and reuse them to improve performance.

This optimization reduces the time spent in
DayPickerRangeController#componentWillReceiveProps when selecting dates
from ~16ms to ~6-10ms.
  • Loading branch information
lencioni committed May 31, 2019
1 parent 2e1dd23 commit a60a76a
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/components/DayPickerRangeController.jsx
Expand Up @@ -40,6 +40,7 @@ import {

import DayPicker from './DayPicker';
import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast';
import getPooledMoment from '../utils/getPooledMoment';

const propTypes = forbidExtraProps({
startDate: momentPropTypes.momentObj,
Expand Down Expand Up @@ -394,7 +395,7 @@ export default class DayPickerRangeController extends React.PureComponent {
if (didFocusChange || recomputePropModifiers) {
values(visibleDays).forEach((days) => {
Object.keys(days).forEach((day) => {
const momentObj = moment(day);
const momentObj = getPooledMoment(day);
let isBlocked = false;

if (didFocusChange || recomputeOutsideRange) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/DayPickerSingleDateController.jsx
Expand Up @@ -31,6 +31,7 @@ import {

import DayPicker from './DayPicker';
import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast';
import getPooledMoment from '../utils/getPooledMoment';

const propTypes = forbidExtraProps({
date: momentPropTypes.momentObj,
Expand Down Expand Up @@ -268,7 +269,7 @@ export default class DayPickerSingleDateController extends React.PureComponent {
if (didFocusChange || recomputePropModifiers) {
values(visibleDays).forEach((days) => {
Object.keys(days).forEach((day) => {
const momentObj = moment(day);
const momentObj = getPooledMoment(day);
if (this.isBlocked(momentObj)) {
modifiers = this.addModifier(modifiers, momentObj, 'blocked');
} else {
Expand Down
10 changes: 10 additions & 0 deletions src/utils/getPooledMoment.js
@@ -0,0 +1,10 @@
import moment from 'moment';

const momentPool = new Map();
export default function getPooledMoment(dayString) {
if (!momentPool.has(dayString)) {
momentPool.set(dayString, moment(dayString));
}

return momentPool.get(dayString);
}
24 changes: 24 additions & 0 deletions test/utils/getPooledMoment_spec.js
@@ -0,0 +1,24 @@
import { expect } from 'chai';
import moment from 'moment';

import getPooledMoment from '../../src/utils/getPooledMoment';

describe('getPooledMoment', () => {
it('returns a moment given a day string', () => {
const momentObj = getPooledMoment('2017-12-10');
expect(moment.isMoment(momentObj)).to.equal(true);
expect(momentObj.format('YYYY MM DD')).to.equal('2017 12 10');
});

it('returns the same moment given the same day string', () => {
const momentObj1 = getPooledMoment('2017-12-10');
const momentObj2 = getPooledMoment('2017-12-10');
expect(momentObj1).to.equal(momentObj2);
});

it('returns a different moment given a different day string', () => {
const momentObj1 = getPooledMoment('2017-12-10');
const momentObj2 = getPooledMoment('2017-12-11');
expect(momentObj1).not.to.equal(momentObj2);
});
});

0 comments on commit a60a76a

Please sign in to comment.