Bug 1494162 - Part 44: Use refs instead of findDOMNode. r=pbro
authorGabriel Luong <gabriel.luong@gmail.com>
Tue, 02 Oct 2018 12:22:32 -0400
changeset 494998 7ce8b77ec351be14e0fa8b499c17b2135f3627a8
parent 494997 04cf22628ca53643fdb9620fe547ae59f45e4b14
child 494999 dce559a7ac350c38dc03c3fbe3b2b5eb91b45a06
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1494162
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1494162 - Part 44: Use refs instead of findDOMNode. r=pbro
devtools/client/framework/components/ToolboxTabs.js
devtools/client/inspector/boxmodel/components/BoxModelMain.js
devtools/client/shared/components/splitter/Draggable.js
devtools/client/shared/components/splitter/SplitBox.js
devtools/client/shared/components/tabs/Tabs.js
--- a/devtools/client/framework/components/ToolboxTabs.js
+++ b/devtools/client/framework/components/ToolboxTabs.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { Component, createFactory, createRef } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const { ToolboxTabsOrderManager } = require("devtools/client/framework/toolbox-tabs-order-manager");
 
 const { div } = dom;
 
 const ToolboxTab = createFactory(require("devtools/client/framework/components/ToolboxTab"));
 
 loader.lazyGetter(this, "MenuButton", function() {
   return createFactory(require("devtools/client/shared/components/menu/MenuButton"));
@@ -46,16 +45,18 @@ class ToolboxTabs extends Component {
   constructor(props) {
     super(props);
 
     this.state = {
       // Array of overflowed tool id.
       overflowedTabIds: [],
     };
 
+    this.wrapperEl = createRef();
+
     // Map with tool Id and its width size. This lifecycle is out of React's
     // lifecycle. If a tool is registered, ToolboxTabs will add target tool id
     // to this map. ToolboxTabs will never remove tool id from this cache.
     this._cachedToolTabsWidthMap = new Map();
 
     this._resizeTimerId = null;
     this.resizeHandler = this.resizeHandler.bind(this);
 
@@ -117,38 +118,36 @@ class ToolboxTabs extends Component {
     const nextPanels = nextProps.panelDefinitions.map(def => def.id);
     return !this.equalToolIdArray(prevPanels, nextPanels);
   }
 
   /**
    * Update the Map of tool id and tool tab width.
    */
   updateCachedToolTabsWidthMap() {
-    const thisNode = findDOMNode(this);
     const utils = window.windowUtils;
     // Force a reflow before calling getBoundingWithoutFlushing on each tab.
-    thisNode.clientWidth;
+    this.wrapperEl.current.clientWidth;
 
-    for (const tab of thisNode.querySelectorAll(".devtools-tab")) {
+    for (const tab of this.wrapperEl.current.querySelectorAll(".devtools-tab")) {
       const tabId = tab.id.replace("toolbox-tab-", "");
       if (!this._cachedToolTabsWidthMap.has(tabId)) {
         const rect = utils.getBoundsWithoutFlushing(tab);
         this._cachedToolTabsWidthMap.set(tabId, rect.width);
       }
     }
   }
 
   /**
    * Update the overflowed tab array from currently displayed tool tab.
    * If calculated result is the same as the current overflowed tab array, this
    * function will not update state.
    */
   updateOverflowedTabs() {
-    const node = findDOMNode(this);
-    const toolboxWidth = parseInt(getComputedStyle(node).width, 10);
+    const toolboxWidth = parseInt(getComputedStyle(this.wrapperEl.current).width, 10);
     const { currentToolId } = this.props;
     const enabledTabs = this.props.panelDefinitions.map(def => def.id);
     let sumWidth = 0;
     const visibleTabs = [];
 
     for (const id of enabledTabs) {
       const width = this._cachedToolTabsWidthMap.get(id);
       sumWidth += width;
@@ -271,17 +270,18 @@ class ToolboxTabs extends Component {
           selectTool,
         });
       }
       return null;
     });
 
     return div(
       {
-        className: "toolbox-tabs-wrapper"
+        className: "toolbox-tabs-wrapper",
+        ref: this.wrapperEl,
       },
       div(
         {
           className: "toolbox-tabs",
           onMouseDown: (e) => this._tabsOrderManager.onMouseDown(e),
         },
         tabs,
         (this.state.overflowedTabIds.length > 0)
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const { KeyCodes } = require("devtools/client/shared/keycodes");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const BoxModelEditable = createFactory(require("./BoxModelEditable"));
 
 const Types = require("../types");
 
 const SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";
@@ -211,17 +210,17 @@ class BoxModelMain extends PureComponent
    *         Node to be observed
    * @param  {Boolean} shiftKey
    *         Determines if shiftKey was pressed
    * @param  {String} level
    *         Current active layout
    */
   moveFocus({ target, shiftKey }, level) {
     const editBoxes = [
-      ...findDOMNode(this).querySelectorAll(`[data-box="${level}"].boxmodel-editable`)
+      ...this.positionLayout.querySelectorAll(`[data-box="${level}"].boxmodel-editable`)
     ];
     const editingMode = target.tagName === "input";
     // target.nextSibling is input field
     let position = editingMode ? editBoxes.indexOf(target.nextSibling)
                                : editBoxes.indexOf(target);
 
     if (position === editBoxes.length - 1 && !shiftKey) {
       position = 0;
--- a/devtools/client/shared/components/splitter/Draggable.js
+++ b/devtools/client/shared/components/splitter/Draggable.js
@@ -1,45 +1,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Component } = require("devtools/client/shared/vendor/react");
+const { createRef, Component } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
 class Draggable extends Component {
   static get propTypes() {
     return {
       onMove: PropTypes.func.isRequired,
       onStart: PropTypes.func,
       onStop: PropTypes.func,
       style: PropTypes.object,
       className: PropTypes.string
     };
   }
 
   constructor(props) {
     super(props);
+
+    this.draggableEl = createRef();
+
     this.startDragging = this.startDragging.bind(this);
     this.onMove = this.onMove.bind(this);
     this.onUp = this.onUp.bind(this);
   }
 
   startDragging(ev) {
     if (this.isDragging) {
       return;
     }
     this.isDragging = true;
 
     ev.preventDefault();
-    const doc = ReactDOM.findDOMNode(this).ownerDocument;
+    const doc = this.draggableEl.current.ownerDocument;
     doc.addEventListener("mousemove", this.onMove);
     doc.addEventListener("mouseup", this.onUp);
     this.props.onStart && this.props.onStart();
   }
 
   onMove(ev) {
     if (!this.isDragging) {
       return;
@@ -53,24 +55,25 @@ class Draggable extends Component {
 
   onUp(ev) {
     if (!this.isDragging) {
       return;
     }
     this.isDragging = false;
 
     ev.preventDefault();
-    const doc = ReactDOM.findDOMNode(this).ownerDocument;
+    const doc = this.draggableEl.current.ownerDocument;
     doc.removeEventListener("mousemove", this.onMove);
     doc.removeEventListener("mouseup", this.onUp);
     this.props.onStop && this.props.onStop();
   }
 
   render() {
     return dom.div({
+      ref: this.draggableEl,
       role: "presentation",
       style: this.props.style,
       className: this.props.className,
       onMouseDown: this.startDragging
     });
   }
 }
 
--- a/devtools/client/shared/components/splitter/SplitBox.js
+++ b/devtools/client/shared/components/splitter/SplitBox.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+
 const Draggable = createFactory(require("devtools/client/shared/components/splitter/Draggable"));
 
 /**
  * This component represents a Splitter. The splitter supports vertical
  * as well as horizontal mode.
  */
 class SplitBox extends Component {
   static get propTypes() {
@@ -134,52 +134,49 @@ class SplitBox extends Component {
   // Dragging Events
 
   /**
    * Set 'resizing' cursor on entire document during splitter dragging.
    * This avoids cursor-flickering that happens when the mouse leaves
    * the splitter bar area (happens frequently).
    */
   onStartMove() {
-    const splitBox = ReactDOM.findDOMNode(this);
-    const doc = splitBox.ownerDocument;
+    const doc = this.splitBox.ownerDocument;
     const defaultCursor = doc.documentElement.style.cursor;
     doc.documentElement.style.cursor = (this.state.vert ? "ew-resize" : "ns-resize");
 
-    splitBox.classList.add("dragging");
+    this.splitBox.classList.add("dragging");
 
     this.setState({
       defaultCursor: defaultCursor
     });
   }
 
   onStopMove() {
-    const splitBox = ReactDOM.findDOMNode(this);
-    const doc = splitBox.ownerDocument;
+    const doc = this.splitBox.ownerDocument;
     doc.documentElement.style.cursor = this.state.defaultCursor;
 
-    splitBox.classList.remove("dragging");
+    this.splitBox.classList.remove("dragging");
   }
 
   /**
    * Adjust size of the controlled panel. Depending on the current
    * orientation we either remember the width or height of
    * the splitter box.
    */
   onMove(x, y) {
-    const node = ReactDOM.findDOMNode(this);
-    const nodeBounds = node.getBoundingClientRect();
+    const nodeBounds = this.splitBox.getBoundingClientRect();
 
     let size;
     let { endPanelControl, vert } = this.state;
 
     if (vert) {
       // Use the document owning the SplitBox to detect rtl. The global document might be
       // the one bound to the toolbox shared BrowserRequire, which is irrelevant here.
-      const doc = node.ownerDocument;
+      const doc = this.splitBox.ownerDocument;
 
       // Switch the control flag in case of RTL. Note that RTL
       // has impact on vertical splitter only.
       if (doc.dir === "rtl") {
         endPanelControl = !endPanelControl;
       }
 
       size = endPanelControl ?
@@ -244,19 +241,23 @@ class SplitBox extends Component {
     }
 
     // Calculate splitter size
     const splitterStyle = {
       flex: "0 0 " + splitterSize + "px"
     };
 
     return (
-      dom.div({
-        className: classNames.join(" "),
-        style: style },
+      dom.div(
+        {
+          className: classNames.join(" "),
+          ref: div => {
+            this.splitBox = div;
+          },
+          style },
         startPanel ?
           dom.div({
             className: endPanelControl ? "uncontrolled" : "controlled",
             style: leftPanelStyle,
             role: "presentation",
             ref: div => {
               this.startPanelContainer = div;
             }},
--- a/devtools/client/shared/components/tabs/Tabs.js
+++ b/devtools/client/shared/components/tabs/Tabs.js
@@ -2,20 +2,19 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function(require, exports, module) {
-  const { Component } = require("devtools/client/shared/vendor/react");
+  const { Component, createRef } = require("devtools/client/shared/vendor/react");
   const dom = require("devtools/client/shared/vendor/react-dom-factories");
   const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-  const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 
   /**
    * Renders simple 'tab' widget.
    *
    * Based on ReactSimpleTabs component
    * https://github.com/pedronauck/react-simpletabs
    *
    * Component markup (+CSS) example:
@@ -85,27 +84,29 @@ define(function(require, exports, module
         // E.g. in case of an iframe being used as a tab-content we want the iframe to
         // stay in the DOM.
         created: [],
 
         // True if tabs can't fit into available horizontal space.
         overflow: false,
       };
 
+      this.tabsEl = createRef();
+
       this.onOverflow = this.onOverflow.bind(this);
       this.onUnderflow = this.onUnderflow.bind(this);
       this.onKeyDown = this.onKeyDown.bind(this);
       this.onClickTab = this.onClickTab.bind(this);
       this.setActive = this.setActive.bind(this);
       this.renderMenuItems = this.renderMenuItems.bind(this);
       this.renderPanels = this.renderPanels.bind(this);
     }
 
     componentDidMount() {
-      const node = findDOMNode(this);
+      const node = this.tabsEl.current;
       node.addEventListener("keydown", this.onKeyDown);
 
       // Register overflow listeners to manage visibility
       // of all-tabs-menu. This menu is displayed when there
       // is not enough h-space to render all tabs.
       // It allows the user to select a tab even if it's hidden.
       if (this.props.showAllTabsMenu) {
         node.addEventListener("overflow", this.onOverflow);
@@ -158,17 +159,17 @@ define(function(require, exports, module
       }
 
       this.setState({
         created,
       });
     }
 
     componentWillUnmount() {
-      const node = findDOMNode(this);
+      const node = this.tabsEl.current;
       node.removeEventListener("keydown", this.onKeyDown);
 
       if (this.props.showAllTabsMenu) {
         node.removeEventListener("overflow", this.onOverflow);
         node.removeEventListener("underflow", this.onUnderflow);
       }
     }
 
@@ -241,18 +242,17 @@ define(function(require, exports, module
 
       const newState = Object.assign({}, this.state, {
         created,
         tabActive: index,
       });
 
       this.setState(newState, () => {
         // Properly set focus on selected tab.
-        const node = findDOMNode(this);
-        const selectedTab = node.querySelector(".is-active > a");
+        const selectedTab = this.tabsEl.current.querySelector(".is-active > a");
         if (selectedTab) {
           selectedTab.focus();
         }
 
         if (onAfterChange) {
           onAfterChange(index);
         }
       });
@@ -405,17 +405,20 @@ define(function(require, exports, module
         dom.div({className: "panels"},
           panels
         )
       );
     }
 
     render() {
       return (
-        dom.div({ className: ["tabs", this.props.className].join(" ") },
+        dom.div({
+          className: ["tabs", this.props.className].join(" "),
+          ref: this.tabsEl,
+        },
           this.renderMenuItems(),
           this.renderPanels()
         )
       );
     }
   }
 
   /**