Repository: laboratoriobridge/bold
Pull request #524 (2020-01-27 16:17:34)

Performance improvements for Calendar and MonthView
https://github.com/laboratoriobridge/bold/issues/523


Patch:
diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx
index cb1541657..18a2b8389 100644
--- a/src/components/Calendar/Calendar.tsx
+++ b/src/components/Calendar/Calendar.tsx
@@ -1,5 +1,5 @@
 import { Interpolation } from 'emotion'
-import React, { CSSProperties, useState } from 'react'
+import React, { CSSProperties, useCallback, useMemo, useState } from 'react'
 
 import { Theme, useStyles } from '../../styles'
 import { Omit } from '../../util/types'
@@ -34,15 +34,23 @@ export function Calendar(props: CalendarProps) {
 
   const [visibleDate, setVisibleDate] = useState(initialVisibleDate)
 
-  const allModifiers = () => ({ ...defaultModifiers, ...modifiers })
-  const allModifierStyles = () => ({ ...defaultModifierStyles, ...modifierStyles })
-
-  const handleDayClick = (day: Date) => {
-    if (!allModifiers().disabled(day, props)) {
-      setVisibleDate(day)
-      return props.onDayClick && props.onDayClick(day)
-    }
-  }
+  const allModifiers = useMemo(() => ({ ...defaultModifiers, ...modifiers }), [modifiers])
+  const allModifierStyles = useMemo(() => ({ ...defaultModifierStyles, ...modifierStyles }), [modifierStyles])
+  const createDayStyles = useMemo(() => createDayStylesFn(allModifiers, allModifierStyles, theme), [
+    allModifiers,
+    allModifierStyles,
+    theme,
+  ])
+
+  const handleDayClick = useCallback(
+    (day: Date) => {
+      if (!allModifiers.disabled(day, props)) {
+        setVisibleDate(day)
+        return props.onDayClick && props.onDayClick(day)
+      }
+    },
+    [allModifiers, props.onDayClick]
+  )
 
   return (
     <div className={classes.root}>
@@ -51,12 +59,7 @@ export function Calendar(props: CalendarProps) {
         <YearControl visibleDate={visibleDate} onChange={setVisibleDate} />
       </HFlow>
 
-      <MonthView
-        createDayStyles={createDayStyles(allModifiers(), allModifierStyles(), theme)}
-        {...rest}
-        visibleDate={visibleDate}
-        onDayClick={handleDayClick}
-      />
+      <MonthView createDayStyles={createDayStyles} {...rest} visibleDate={visibleDate} onDayClick={handleDayClick} />
     </div>
   )
 }
@@ -119,7 +122,7 @@ export const defaultModifierStyles: DayModifierStyleMap = {
   }),
 }
 
-export const createDayStyles = (modifiers: DayModifierPredicateMap, styles: DayModifierStyleMap, theme: Theme) => (
+export const createDayStylesFn = (modifiers: DayModifierPredicateMap, styles: DayModifierStyleMap, theme: Theme) => (
   day: Date,
   props: MonthViewProps
 ): Interpolation => {
diff --git a/src/components/Calendar/MonthView.tsx b/src/components/Calendar/MonthView.tsx
index 6e42fc20f..9db51aa91 100644
--- a/src/components/Calendar/MonthView.tsx
+++ b/src/components/Calendar/MonthView.tsx
@@ -1,5 +1,5 @@
 import { Interpolation } from 'emotion'
-import React, { CSSProperties } from 'react'
+import React, { CSSProperties, useCallback, useMemo } from 'react'
 
 import { Theme, useStyles } from '../../styles'
 import { getUserLocale } from '../../util/locale'
@@ -17,11 +17,11 @@ export interface MonthViewProps {
 
 export function MonthView(props: MonthViewProps) {
   const { visibleDate, renderDay, renderWeekName, createDayStyles, onDayClick, onDayHover } = props
-  const month = createMonthMatrix(visibleDate)
   const { classes, css } = useStyles(createStyles)
 
-  const handleDayClick = (day: Date) => () => onDayClick(day)
-  const handleDayHover = (day: Date) => () => onDayHover(day)
+  const month = useMemo(() => createMonthMatrix(visibleDate), [visibleDate])
+  const handleDayClick = useCallback((day: Date) => () => onDayClick(day), [onDayClick])
+  const handleDayHover = useCallback((day: Date) => () => onDayHover(day), [onDayHover])
 
   return (
     <table className={classes.table}>
diff --git a/yarn.lock b/yarn.lock
index 45323fa2c..54a7ec0f7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1933,11 +1933,6 @@
     "@testing-library/dom" "^6.11.0"
     "@types/testing-library__react" "^9.1.2"
 
-"@testing-library/user-event@8.0.3":
-  version "8.0.3"
-  resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-8.0.3.tgz#c3e304d2692b797ff0e3ced53c0597d341bb868b"
-  integrity sha512-ALoIj6GzxN0UzFiudSwENy2yTD1xHDW+KtuOch5xuqRgQr0AxGB8z+EQBxT6zlWgAoLGN8hmYxTTGRoQSOZtlg==
-
 "@types/babel__core@^7.1.0":
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30"


Test patch:
diff --git a/src/components/Calendar/Calendar.test.tsx b/src/components/Calendar/Calendar.test.tsx
index 4ba7ebdab..3d506deb3 100644
--- a/src/components/Calendar/Calendar.test.tsx
+++ b/src/components/Calendar/Calendar.test.tsx
@@ -4,7 +4,7 @@ import React from 'react'
 import { createTheme } from '../../styles'
 
 import * as CalendarModule from './Calendar'
-import { Calendar, createDayStyles, defaultModifiers, defaultModifierStyles } from './Calendar'
+import { Calendar, createDayStylesFn, defaultModifiers, defaultModifierStyles } from './Calendar'
 import { isSameDay } from './util'
 
 describe('Calendar', () => {
@@ -58,7 +58,7 @@ describe('Calendar', () => {
   })
 
   it('should accept modifiers and modifierStyles props', () => {
-    const spy = jest.spyOn(CalendarModule, 'createDayStyles')
+    const spy = jest.spyOn(CalendarModule, 'createDayStylesFn')
     const customModifiers = {
       today: () => true,
       custom: () => false,
@@ -131,10 +131,10 @@ describe('modifierStyles', () => {
   })
 })
 
-describe('createDayStyles', () => {
+describe('createDayStylesFn', () => {
   it('should return merged styles from all modifiers that apply', () => {
     const theme = createTheme()
-    const stylesCreator = createDayStyles(
+    const stylesCreator = createDayStylesFn(
       {
         today: () => true,
         disabled: () => true,
diff --git a/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap b/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap
index 2b7111299..9d2d2b012 100644
--- a/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap
+++ b/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap
@@ -644,7 +644,7 @@ exports[`Calendar should render correctly 1`] = `
 </div>
 `;
 
-exports[`createDayStyles should return merged styles from all modifiers that apply 1`] = `
+exports[`createDayStylesFn should return merged styles from all modifiers that apply 1`] = `
 Object {
   ":hover": Object {
     "background": "#0069D0",