From 656314f90498c55dde4a9121ad733401efb00c6f Mon Sep 17 00:00:00 2001
From: Jack Reed <phillipjreed@gmail.com>
Date: Tue, 2 Apr 2019 12:30:18 -0600
Subject: [PATCH] Pass params using DOM Apis to prevent rerender by function
 prop

See https://reactjs.org/docs/faq-functions.html#how-do-i-pass-a-parameter-to-an-event-handler-or-callback

Second example
---
 src/components/CompanionArea.js             | 14 ++++++++++-
 src/components/CompanionWindow.js           | 22 +++++++++++++---
 src/components/GalleryView.js               | 19 +++++++++++---
 src/components/LanguageSettings.js          | 17 +++++++++++--
 src/components/ManifestListItemError.js     | 25 +++++++++++++++---
 src/components/WindowThumbnailSettings.js   | 27 ++++++++++++++------
 src/components/WindowViewSettings.js        | 24 ++++++++++++------
 src/components/WorkspaceAdd.js              | 28 +++++++++++++++------
 src/components/WorkspaceAddButton.js        | 19 ++++++++++++--
 src/components/WorkspaceFullScreenButton.js | 16 ++++++++++--
 src/components/WorkspaceMosaic.js           | 22 ++++++++++------
 src/components/ZoomControls.js              | 11 ++++++--
 12 files changed, 196 insertions(+), 48 deletions(-)

diff --git a/src/components/CompanionArea.js b/src/components/CompanionArea.js
index a14505e44..3646afa4d 100644
--- a/src/components/CompanionArea.js
+++ b/src/components/CompanionArea.js
@@ -8,6 +8,18 @@ import ns from '../config/css-ns';
 
 /** */
 export class CompanionArea extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+    this.setCompanionAreaOpen = this.setCompanionAreaOpen.bind(this);
+  }
+
+  /** */
+  setCompanionAreaOpen() {
+    const { setCompanionAreaOpen, windowId, companionAreaOpen } = this.props;
+    setCompanionAreaOpen(windowId, !companionAreaOpen);
+  }
+
   /** */
   areaLayoutClass() {
     const {
@@ -32,7 +44,7 @@ export class CompanionArea extends Component {
             <MiradorMenuButton
               aria-label={companionAreaOpen ? t('collapseSidePanel') : t('expandSidePanel')}
               className={classes.toggle}
-              onClick={() => { setCompanionAreaOpen(windowId, !companionAreaOpen); }}
+              onClick={this.setCompanionAreaOpen}
               TooltipProps={{
                 placement: 'right',
                 style: {
diff --git a/src/components/CompanionWindow.js b/src/components/CompanionWindow.js
index e9d320300..17a2ff147 100644
--- a/src/components/CompanionWindow.js
+++ b/src/components/CompanionWindow.js
@@ -14,14 +14,26 @@ import ns from '../config/css-ns';
  * CompanionWindow
  */
 export class CompanionWindow extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+    this.updateCompanionWindow = this.updateCompanionWindow.bind(this);
+  }
+
+  /** */
+  updateCompanionWindow(e) {
+    const { id, updateCompanionWindow, windowId } = this.props;
+    updateCompanionWindow(windowId, id, e.currentTarget.dataset.position);
+  }
+
   /**
    * render
    * @return
    */
   render() {
     const {
-      classes, paperClassName, id, onCloseClick, updateCompanionWindow, isDisplayed,
-      position, t, windowId, title, children, titleControls,
+      classes, paperClassName, onCloseClick, updateCompanionWindow, isDisplayed,
+      position, t, title, children, titleControls,
     } = this.props;
 
     return (
@@ -52,7 +64,8 @@ export class CompanionWindow extends Component {
                 && (
                   <MiradorMenuButton
                     aria-label={t('openInCompanionWindow')}
-                    onClick={() => { updateCompanionWindow(windowId, id, { position: 'right' }); }}
+                    data-position="right"
+                    onClick={this.updateCompanionWindow}
                   >
                     <OpenInNewIcon />
                   </MiradorMenuButton>
@@ -63,8 +76,9 @@ export class CompanionWindow extends Component {
                     updateCompanionWindow && (
                       <MiradorMenuButton
                         aria-label={position === 'bottom' ? t('moveCompanionWindowToRight') : t('moveCompanionWindowToBottom')}
+                        data-position={position === 'bottom' ? 'right' : 'bottom'}
                         wrapperClassName={classes.positionButton}
-                        onClick={() => { updateCompanionWindow(windowId, id, { position: position === 'bottom' ? 'right' : 'bottom' }); }}
+                        onClick={this.updateCompanionWindow}
                       >
                         {position === 'bottom' ? <ThumbnailNavigationRightIcon /> : <ThumbnailNavigationBottomIcon />}
                       </MiradorMenuButton>
diff --git a/src/components/GalleryView.js b/src/components/GalleryView.js
index b83e30652..189dc3d34 100644
--- a/src/components/GalleryView.js
+++ b/src/components/GalleryView.js
@@ -10,12 +10,24 @@ import { CanvasThumbnail } from './CanvasThumbnail';
  * OSD and Navigation
  */
 export class GalleryView extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+    this.setCanvas = this.setCanvas.bind(this);
+  }
+
+  /** */
+  setCanvas(e) {
+    const { setCanvas, window } = this.props;
+    setCanvas(window.id, e.currentTarget.dataset.canvasIndex);
+  }
+
   /**
    * Renders things
    */
   render() {
     const {
-      canvases, classes, setCanvas, window,
+      canvases, classes, window,
     } = this.props;
     return (
       <>
@@ -35,8 +47,9 @@ export class GalleryView extends Component {
                       canvas.index === window.canvasIndex ? classes.currentCanvas : '',
                     )
                   }
-                  onClick={() => setCanvas(window.id, canvas.index)}
-                  onKeyUp={() => setCanvas(window.id, canvas.index)}
+                  data-canvas-index={canvas.index}
+                  onClick={this.setCanvas}
+                  onKeyUp={this.setCanvas}
                   role="button"
                   tabIndex={0}
                 >
diff --git a/src/components/LanguageSettings.js b/src/components/LanguageSettings.js
index 51a02be6f..13edaeff2 100644
--- a/src/components/LanguageSettings.js
+++ b/src/components/LanguageSettings.js
@@ -11,12 +11,24 @@ import PropTypes from 'prop-types';
  * of the application
 */
 export class LanguageSettings extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+    this.handleClick = this.handleClick.bind(this);
+  }
+
+  /** */
+  handleClick(e) {
+    const { handleClick } = this.props;
+    handleClick(e.currentTarget.dataset.locale);
+  }
+
   /**
    * Returns the rendered component
   */
   render() {
     const {
-      handleClick, languages,
+      languages,
     } = this.props;
 
     return (
@@ -26,7 +38,8 @@ export class LanguageSettings extends Component {
             <MenuItem
               button={!language.current}
               key={language.locale}
-              onClick={() => { handleClick(language.locale); }}
+              data-locale={language.locale}
+              onClick={this.handleClick}
             >
               {
                 language.current
diff --git a/src/components/ManifestListItemError.js b/src/components/ManifestListItemError.js
index 9488ad293..4533731ff 100644
--- a/src/components/ManifestListItemError.js
+++ b/src/components/ManifestListItemError.js
@@ -10,12 +10,31 @@ import Typography from '@material-ui/core/Typography';
  * message to the user about a problem loading a manifest
 */
 export class ManifestListItemError extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+    this.onDismissClick = this.onDismissClick.bind(this);
+    this.onTryAgainClick = this.onTryAgainClick.bind(this);
+  }
+
+  /** */
+  onDismissClick() {
+    const { onDismissClick, manifestId } = this.props;
+    onDismissClick(manifestId);
+  }
+
+  /** */
+  onTryAgainClick() {
+    const { onTryAgainClick, manifestId } = this.props;
+    onTryAgainClick(manifestId);
+  }
+
   /**
    * Returns the rendered component
   */
   render() {
     const {
-      classes, manifestId, onDismissClick, onTryAgainClick, t,
+      classes, manifestId, t,
     } = this.props;
 
     return (
@@ -37,10 +56,10 @@ export class ManifestListItemError extends Component {
         <Grid container>
           <Grid container item xs={12} sm={6} justify="flex-end">
             <Grid item>
-              <Button onClick={() => { onDismissClick(manifestId); }}>
+              <Button onClick={this.onDismissClick}>
                 {t('dismiss')}
               </Button>
-              <Button onClick={() => { onTryAgainClick(manifestId); }}>
+              <Button onClick={this.onTryAgainClick}>
                 {t('tryAgain')}
               </Button>
             </Grid>
diff --git a/src/components/WindowThumbnailSettings.js b/src/components/WindowThumbnailSettings.js
index 0e63933a8..957764cc0 100644
--- a/src/components/WindowThumbnailSettings.js
+++ b/src/components/WindowThumbnailSettings.js
@@ -21,10 +21,11 @@ export class WindowThumbnailSettings extends Component {
   /**
    * @private
    */
-  handleChange(value) {
-    const { windowId, setWindowThumbnailPosition } = this.props;
+  handleChange(e) {
+    const { windowId, setWindowThumbnailPosition, handleClose } = this.props;
 
-    setWindowThumbnailPosition(windowId, value);
+    setWindowThumbnailPosition(windowId, e.currentTarget.dataset.value);
+    handleClose();
   }
 
   /**
@@ -34,14 +35,18 @@ export class WindowThumbnailSettings extends Component {
    */
   render() {
     const {
-      classes, handleClose, t, thumbnailNavigationPosition,
+      classes, t, thumbnailNavigationPosition,
     } = this.props;
 
     return (
       <>
         <ListSubheader role="presentation" tabIndex="-1">{t('thumbnails')}</ListSubheader>
 
-        <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('off'); handleClose(); }}>
+        <MenuItem
+          className={classes.MenuItem}
+          data-value="off"
+          onClick={this.handleChange}
+        >
           <FormControlLabel
             value="off"
             classes={{ label: thumbnailNavigationPosition === 'off' ? classes.selectedLabel : undefined }}
@@ -52,7 +57,11 @@ export class WindowThumbnailSettings extends Component {
             labelPlacement="bottom"
           />
         </MenuItem>
-        <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('far-bottom'); handleClose(); }}>
+        <MenuItem
+          className={classes.MenuItem}
+          data-value="far-bottom"
+          onClick={this.handleChange}
+        >
           <FormControlLabel
             value="far-bottom"
             classes={{ label: thumbnailNavigationPosition === 'far-bottom' ? classes.selectedLabel : undefined }}
@@ -63,7 +72,11 @@ export class WindowThumbnailSettings extends Component {
             labelPlacement="bottom"
           />
         </MenuItem>
-        <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('far-right'); handleClose(); }}>
+        <MenuItem
+          className={classes.MenuItem}
+          data-value="far-right"
+          onClick={this.handleChange}
+        >
           <FormControlLabel
             value="far-right"
             classes={{ label: thumbnailNavigationPosition === 'far-right' ? classes.selectedLabel : undefined }}
diff --git a/src/components/WindowViewSettings.js b/src/components/WindowViewSettings.js
index e510446f1..64f8e5a98 100644
--- a/src/components/WindowViewSettings.js
+++ b/src/components/WindowViewSettings.js
@@ -42,10 +42,11 @@ export class WindowViewSettings extends Component {
   /**
    * @private
    */
-  handleChange(value) {
-    const { windowId, setWindowViewType } = this.props;
+  handleChange(e) {
+    const { windowId, setWindowViewType, handleClose } = this.props;
 
-    setWindowViewType(windowId, value);
+    setWindowViewType(windowId, e.currentTarget.dataset.view);
+    handleClose();
   }
 
   /**
@@ -55,7 +56,7 @@ export class WindowViewSettings extends Component {
    */
   render() {
     const {
-      classes, handleClose, t, windowViewType,
+      classes, t, windowViewType,
     } = this.props;
 
     return (
@@ -65,7 +66,8 @@ export class WindowViewSettings extends Component {
         <MenuItem
           className={classes.MenuItem}
           ref={ref => this.handleSelectedRef(ref)}
-          onClick={() => { this.handleChange('single'); handleClose(); }}
+          data-view="single"
+          onClick={this.handleChange}
         >
           <FormControlLabel
             value="single"
@@ -75,7 +77,11 @@ export class WindowViewSettings extends Component {
             labelPlacement="bottom"
           />
         </MenuItem>
-        <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('book'); handleClose(); }}>
+        <MenuItem
+          className={classes.MenuItem}
+          data-view="book"
+          onClick={this.handleChange}
+        >
           <FormControlLabel
             value="book"
             classes={{ label: windowViewType === 'book' ? classes.selectedLabel : undefined }}
@@ -84,7 +90,11 @@ export class WindowViewSettings extends Component {
             labelPlacement="bottom"
           />
         </MenuItem>
-        <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('gallery'); handleClose(); }}>
+        <MenuItem
+          className={classes.MenuItem}
+          data-view="gallery"
+          onClick={this.handleChange}
+        >
           <FormControlLabel
             value="gallery"
             classes={{ label: windowViewType === 'gallery' ? classes.selectedLabel : undefined }}
diff --git a/src/components/WorkspaceAdd.js b/src/components/WorkspaceAdd.js
index da505ab08..9aa9c12ce 100644
--- a/src/components/WorkspaceAdd.js
+++ b/src/components/WorkspaceAdd.js
@@ -28,13 +28,22 @@ export class WorkspaceAdd extends React.Component {
     this.state = { addResourcesOpen: false };
 
     this.setAddResourcesVisibility = this.setAddResourcesVisibility.bind(this);
+    this.setWorkspaceAddVisibility = this.setWorkspaceAddVisibility.bind(this);
   }
 
   /**
    * @private
    */
-  setAddResourcesVisibility(bool) {
-    this.setState({ addResourcesOpen: bool });
+  setAddResourcesVisibility(e) {
+    this.setState({ addResourcesOpen: e.currentTarget.dataset.resourceVisiblity });
+  }
+
+  /**
+   * @private
+   */
+  setWorkspaceAddVisibility(e) {
+    const { setWorkspaceAddVisibility } = this.props;
+    setWorkspaceAddVisibility(e.currentTarget.dataset.workspaceVisibility);
   }
 
   /**
@@ -42,7 +51,7 @@ export class WorkspaceAdd extends React.Component {
    */
   render() {
     const {
-      manifests, setWorkspaceAddVisibility, t, classes,
+      manifests, t, classes,
     } = this.props;
     const { addResourcesOpen } = this.state;
 
@@ -50,7 +59,8 @@ export class WorkspaceAdd extends React.Component {
       <ManifestListItem
         key={manifest}
         manifestId={manifest}
-        handleClose={() => setWorkspaceAddVisibility(false)}
+        data-workspace-visibility={false}
+        handleClose={this.setWorkspaceAddVisibility}
       />
     ));
 
@@ -68,7 +78,8 @@ export class WorkspaceAdd extends React.Component {
           disabled={addResourcesOpen}
           className={classNames(classes.fab, ns('add-resource-button'))}
           color="secondary"
-          onClick={() => (this.setAddResourcesVisibility(true))}
+          data-resource-visibility
+          onClick={this.setAddResourcesVisibility}
         >
           <AddIcon />
           {t('addResource')}
@@ -91,7 +102,7 @@ export class WorkspaceAdd extends React.Component {
           <Paper
             className={classes.form}
           >
-            <AppBar position="absolute" color="secondary" onClick={() => (this.setAddResourcesVisibility(false))}>
+            <AppBar position="absolute" color="secondary" onClick={this.setAddResourcesVisibility} data-resource-visibility={false}>
               <Toolbar variant="dense">
                 <MiradorMenuButton
                   aria-label={t('closeAddResourceMenu')}
@@ -106,8 +117,9 @@ export class WorkspaceAdd extends React.Component {
             </AppBar>
             <ManifestForm
               addResourcesOpen={addResourcesOpen}
-              onSubmit={() => (this.setAddResourcesVisibility(false))}
-              onCancel={() => (this.setAddResourcesVisibility(false))}
+              data-resource-visibility={false}
+              onSubmit={this.setAddResourcesVisibility}
+              onCancel={this.setAddResourcesVisibility}
             />
           </Paper>
         </Drawer>
diff --git a/src/components/WorkspaceAddButton.js b/src/components/WorkspaceAddButton.js
index 96c8c785d..864d5627e 100644
--- a/src/components/WorkspaceAddButton.js
+++ b/src/components/WorkspaceAddButton.js
@@ -8,13 +8,28 @@ import CloseIcon from '@material-ui/icons/CloseSharp';
 /**
  */
 export class WorkspaceAddButton extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+
+    this.setWorkspaceAddVisibility = this.setWorkspaceAddVisibility.bind(this);
+  }
+
+  /**
+   * @private
+   */
+  setWorkspaceAddVisibility() {
+    const { isWorkspaceAddVisible, setWorkspaceAddVisibility } = this.props;
+    setWorkspaceAddVisibility(!isWorkspaceAddVisible);
+  }
+
   /**
    * render
    * @return
    */
   render() {
     const {
-      classes, t, setWorkspaceAddVisibility, isWorkspaceAddVisible,
+      classes, t, isWorkspaceAddVisible,
     } = this.props;
     return (
       <Tooltip title={isWorkspaceAddVisible ? t('closeAddResourceMenu') : t('addResource')}>
@@ -24,7 +39,7 @@ export class WorkspaceAddButton extends Component {
           id="addBtn"
           aria-label={isWorkspaceAddVisible ? t('closeAddResourceMenu') : t('addResource')}
           className={classes.fab}
-          onClick={() => { setWorkspaceAddVisibility(!isWorkspaceAddVisible); }}
+          onClick={this.setWorkspaceAddVisibility}
         >
           {
             isWorkspaceAddVisible
diff --git a/src/components/WorkspaceFullScreenButton.js b/src/components/WorkspaceFullScreenButton.js
index 2d5f58767..4efd1757d 100644
--- a/src/components/WorkspaceFullScreenButton.js
+++ b/src/components/WorkspaceFullScreenButton.js
@@ -6,19 +6,31 @@ import MiradorMenuButton from '../containers/MiradorMenuButton';
 /**
  */
 export class WorkspaceFullScreenButton extends Component {
+  /**  */
+  constructor(props) {
+    super(props);
+    this.setWorkspaceFullscreen = this.setWorkspaceFullscreen.bind(this);
+  }
+
+  /** */
+  setWorkspaceFullscreen() {
+    const { setWorkspaceFullscreen, isFullscreenEnabled } = this.props;
+    setWorkspaceFullscreen(!isFullscreenEnabled);
+  }
+
   /**
    * render
    * @return
    */
   render() {
     const {
-      classes, isFullscreenEnabled, setWorkspaceFullscreen, t,
+      classes, isFullscreenEnabled, t,
     } = this.props;
     return (
       <MiradorMenuButton
         aria-label={isFullscreenEnabled ? t('exitFullScreen') : t('workspaceFullScreen')}
         className={classes.ctrlBtn}
-        onClick={() => setWorkspaceFullscreen(!isFullscreenEnabled)}
+        onClick={this.setWorkspaceFullscreen}
       >
         {isFullscreenEnabled ? <FullscreenExitIcon /> : <FullscreenIcon />}
       </MiradorMenuButton>
diff --git a/src/components/WorkspaceMosaic.js b/src/components/WorkspaceMosaic.js
index 1c7102dd4..57f459670 100644
--- a/src/components/WorkspaceMosaic.js
+++ b/src/components/WorkspaceMosaic.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import {
   Mosaic, MosaicWindow, getLeaves, createBalancedTreeFromLeaves,
@@ -12,7 +12,7 @@ import Window from '../containers/Window';
  * @memberof Workspace
  * @private
  */
-export class WorkspaceMosaic extends React.Component {
+export class WorkspaceMosaic extends Component {
   /**
    */
   constructor(props) {
@@ -21,6 +21,7 @@ export class WorkspaceMosaic extends React.Component {
     this.tileRenderer = this.tileRenderer.bind(this);
     this.mosaicChange = this.mosaicChange.bind(this);
     this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this);
+    this.renderPreview = this.renderPreview.bind(this);
     this.zeroStateView = <div />;
   }
 
@@ -76,11 +77,7 @@ export class WorkspaceMosaic extends React.Component {
         additionalControls={[]}
         path={path}
         windowId={window.id}
-        renderPreview={() => (
-          <div className="mosaic-preview">
-            <MosaicRenderPreview windowId={window.id} />
-          </div>
-        )}
+        renderPreview={this.renderPreview}
       >
         <Window
           key={window.id}
@@ -98,6 +95,17 @@ export class WorkspaceMosaic extends React.Component {
     updateWorkspaceMosaicLayout(newLayout);
   }
 
+  /* eslint-disable class-methods-use-this */
+  /** */
+  renderPreview(props) {
+    return (
+      <div className="mosaic-preview">
+        <MosaicRenderPreview windowId={props.windowId} />
+      </div>
+    );
+  }
+  /* eslint-enable class-methods-use-this */
+
   /**
    */
   render() {
diff --git a/src/components/ZoomControls.js b/src/components/ZoomControls.js
index ab0a9bb75..2ce290571 100644
--- a/src/components/ZoomControls.js
+++ b/src/components/ZoomControls.js
@@ -16,6 +16,7 @@ export class ZoomControls extends Component {
 
     this.handleZoomInClick = this.handleZoomInClick.bind(this);
     this.handleZoomOutClick = this.handleZoomOutClick.bind(this);
+    this.zoomToWorld = this.zoomToWorld.bind(this);
   }
 
   /**
@@ -44,13 +45,19 @@ export class ZoomControls extends Component {
     });
   }
 
+  /** */
+  zoomToWorld() {
+    const { zoomToWorld } = this.props;
+    zoomToWorld(false);
+  }
+
   /**
    * render
    * @return
    */
   render() {
     const {
-      showZoomControls, classes, t, zoomToWorld,
+      showZoomControls, classes, t,
     } = this.props;
 
     if (!showZoomControls) {
@@ -67,7 +74,7 @@ export class ZoomControls extends Component {
         <MiradorMenuButton aria-label={t('zoomOut')} onClick={this.handleZoomOutClick}>
           <RemoveCircleIcon />
         </MiradorMenuButton>
-        <MiradorMenuButton aria-label={t('zoomReset')} onClick={() => zoomToWorld(false)}>
+        <MiradorMenuButton aria-label={t('zoomReset')} onClick={this.zoomToWorld}>
           <RestoreZoomIcon />
         </MiradorMenuButton>
       </div>
-- 
GitLab