From 8e4f71bbc95f0950db709186c976b501a3191de5 Mon Sep 17 00:00:00 2001 From: Todd Dembrey Date: Mon, 10 Feb 2020 07:55:15 +0000 Subject: [PATCH] Allow Component to handle all event listeners without binding external Wrap all events in a single handler which will delegate out the events to the provided functions. This means that if the external function changes we do not need to rebind the function, fixing issues with functional react components. --- .../CornerstoneViewport.js | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/CornerstoneViewport/CornerstoneViewport.js b/src/CornerstoneViewport/CornerstoneViewport.js index db1f2b7..9b5b7f5 100644 --- a/src/CornerstoneViewport/CornerstoneViewport.js +++ b/src/CornerstoneViewport/CornerstoneViewport.js @@ -120,9 +120,10 @@ class CornerstoneViewport extends Component { isFlippedHorizontally: undefined, }; + this._validateExternalEventsListeners(); + // TODO: Deep Copy? How does that work w/ handlers? // Save a copy. Props could change before `willUnmount` - this.eventListeners = this.props.eventListeners; this.startLoadHandler = this.props.startLoadHandler; this.endLoadHandler = this.props.endLoadHandler; this.loadHandlerTimeout = undefined; // "Loading..." timer @@ -286,6 +287,8 @@ class CornerstoneViewport extends Component { if (Object.keys(updatedState).length > 0) { this.setState(updatedState); } + + this._validateExternalEventsListeners(); } /** @@ -470,29 +473,40 @@ class CornerstoneViewport extends Component { } /** + * Listens out for all events and then defers handling to a single listener to act on them * * @param {string} target - "cornerstone" || "element" * @param {boolean} [clear=false] - True to clear event listeners * @returns {undefined} */ - _bindExternalEventListeners(target, clear = false) { - if (!this.eventListeners) { - return; - } + _bindExternalEventListeners(targetType, clear=false) { + const addOrRemoveEventListener = clear + ? 'removeEventListener' + : 'addEventListener'; const cornerstoneEvents = Object.values(cornerstone.EVENTS); const cornerstoneToolsEvents = Object.values(cornerstoneTools.EVENTS); - const addOrRemoveEventListener = clear - ? 'removeEventListener' - : 'addEventListener'; - - for (let i = 0; i < this.eventListeners.length; i++) { - const { target: targetType, eventName, handler } = this.eventListeners[i]; - if (targetType !== target) { continue; } + const events = cornerstoneEvents.concat(cornerstoneToolsEvents); + const targetElementOrCornerstone = + targetType === 'element' ? this.element : cornerstone.events; + const boundMethod = this._handleExternalEventListeners.bind(this); + for (let i = 0; i < events.length; i++) { + const targetHandler = targetElementOrCornerstone[addOrRemoveEventListener]; + targetHandler(events[i], boundMethod); + } + } - const targetElementOrCornerstone = - targetType === 'element' ? this.element : cornerstone.events; + /** + * Called to validate that events passed into the event listeners prop are valid + * + * @returns {undefined} + */ + _validateExternalEventsListeners() { + const cornerstoneEvents = Object.values(cornerstone.EVENTS); + const cornerstoneToolsEvents = Object.values(cornerstoneTools.EVENTS); + for (let i = 0; i < this.props.eventListeners.length; i++) { + const { target: targetType, eventName, handler } = this.props.eventListeners[i]; if ( !cornerstoneEvents.includes(eventName) && !cornerstoneToolsEvents.includes(eventName) @@ -502,8 +516,25 @@ class CornerstoneViewport extends Component { ); continue; } + } + } + /** + * Handles delegating of events from cornerstone back to the defined + * external events handlers + * + * @param {event} + * @returns {undefined} + */ + _handleExternalEventListeners(event){ + if (!this.props.eventListeners) { + return; + } + for (let i = 0; i < this.props.eventListeners.length; i++) { + const { eventName, handler } = this.props.eventListeners[i]; - targetElementOrCornerstone[addOrRemoveEventListener](eventName, handler); + if (event.type === eventName) { + handler(event); + } } }