Repository: mui/material-ui (TypeScript)
Date: 2019-04-02 23:23:30+00:00
Task type: Bug Fix

Issue:
Use forwardRef
Hi,

It seems that all component functions are "hidden" when exported through the `withStyles` method. 

- [ x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior
Given a component *App*, using component *MainDrawer* that have a function *toggleDrawer*. When setting `ref` attribute on the use of *MainDrawer* (`<MainDrawer ref={(drw) => { this.mainDrawer = drw; }}  />`) inside the render method in *App*,  I should be able to call `this.mainDrawer.toggleDrawer()` inside component *App*.

## Current Behavior
When dumping `this` in component *App* I see  the *mainDrawer* component that was added. But it doesn't contain the *toggleDrawer* function for some reason. If i remove the `withStyles` wrapper from component *MainDrawer*, then the *toggleDrawer* function appears in *this.mainDrawer* and can be executed.

The error I get in console is:
```
Uncaught TypeError: this.mainDrawer.toggleDrawer is not a function
    at App.doToggle (App.js:27)
    at Object.toggle (App.js:35)
    at onClick (AppBarHeader.js:31)
    at HTMLUnknownElement.callCallback (react-dom.development.js:542)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:581)
    at Object.invokeGuardedCallback (react-dom.development.js:438)
    at Object.invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:452)
    at executeDispatch (react-dom.development.js:836)
    at executeDispatchesInOrder (react-dom.development.js:858)
    at executeDispatchesAndRelease (react-dom.development.js:956)
    at executeDispatchesAndReleaseTopLevel (react-dom.development.js:967)
    at Array.forEach (<anonymous>)
    at forEachAccumulated (react-dom.development.js:935)
    at processEventQueue (react-dom.development.js:1112)
    at runEventQueueInBatch (react-dom.development.js:3607)
    at handleTopLevel (react-dom.development.js:3616)
    at handleTopLevelImpl (react-dom.development.js:3347)
    at batchedUpdates (react-dom.development.js:11082)
    at batchedUpdates (react-dom.development.js:2330)
    at dispatchEvent (react-dom.development.js:3421)
```

This leads me to expect that something is going on in `withStyles`, that leads this function to be hidden? Or maybe I have misunderstood it all.

## Steps to Reproduce (for bugs)

https://github.com/lazee/material-ui-issue-withstyles

* Clone
* yarn install
* yarn dev
* Click on the menu icon in the page that appears and look in console.

## Your Environment


| Tech         | Version |
|--------------|---------|
| Material-UI  |  latest next   |
| React        |  16.2.0       |
| browser      |     Chrome 65    |
| Node          |  9.9.0       |



Issue discussion:
> this.mainDrawer.toggleDrawer()

@lazee Calling imperative methods for controlling elements is a discouraged pattern in React. I would encourage you to use an `open` like property.

> It doesn't contain the toggleDrawer function for some reason.

It's because the `withStyles()` higher order component is exposing a different component. You have to use the [documented](https://material-ui-next.com/customization/css-in-js/#api) `innerRef` property.

Let's see if we can use [`forwardRef`](https://github.com/facebook/react/pull/12346) in the future to improve the story.
https://reactjs.org/blog/2018/03/29/react-v-16-3.html#forwardref-api
@oliviertassinari Thank you so much for taking the time to answer something that was already documented. I actually looked for it on the site, but must have done a terrible job. My bad.

I did realize that I was using an anti-pattern, but couldn't figure out why it didn't work. With other wrapper from other frameworks I haven't had the same issues. But what you say about "exposing a different component" makes sense. I will rewrite and use property instead.

`forwardRef` seems like a perfect match for theming. Also looking forward to see what can be done wi th that in the future.

AND: Thank you for Material UI. Love it!
Let's wait and see how these libraries solve the problem:
- https://github.com/reactjs/react-redux/issues/914
- https://github.com/ReactTraining/react-router/issues/6056
`innerRef` doesn't solve the problem of forwarding ref through multiple HOCs, but `forwardRef` does.
Also it would be possible to have both of them available to maintain backward compatibility (I guess).
@alimo You are right, having following the threads of https://github.com/mui-org/material-ui/issues/10825#issuecomment-383318055. It seems that bringing `forwardRef` is not trivial.

> When you start using forwardRef in a component library, you should treat it as a breaking change and release a new major version of your library.

Solution patch:
diff --git a/packages/material-ui/src/Collapse/Collapse.js b/packages/material-ui/src/Collapse/Collapse.js
--- a/packages/material-ui/src/Collapse/Collapse.js
+++ b/packages/material-ui/src/Collapse/Collapse.js
@@ -7,6 +7,7 @@ import Transition from 'react-transition-group/Transition';
 import withStyles from '../styles/withStyles';
 import { duration } from '../styles/transitions';
 import { getTransitionProps } from '../transitions/utils';
+import withForwardedRef from '../utils/withForwardedRef';
 
 export const styles = theme => ({
   /* Styles applied to the container element. */
@@ -132,6 +133,7 @@ class Collapse extends React.Component {
       collapsedHeight,
       component: Component,
       in: inProp,
+      innerRef,
       onEnter,
       onEntered,
       onEntering,
@@ -169,6 +171,7 @@ class Collapse extends React.Component {
               minHeight: collapsedHeight,
               ...style,
             }}
+            ref={innerRef}
             {...childProps}
           >
             <div
@@ -213,6 +216,11 @@ Collapse.propTypes = {
    * If `true`, the component will transition in.
    */
   in: PropTypes.bool,
+  /**
+   * @ignore
+   * from `withForwardedRef`
+   */
+  innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
   /**
    * @ignore
    */
@@ -265,4 +273,4 @@ Collapse.muiSupportAuto = true;
 export default withStyles(styles, {
   withTheme: true,
   name: 'MuiCollapse',
-})(Collapse);
+})(withForwardedRef(Collapse));
diff --git a/packages/material-ui/src/Popper/Popper.js b/packages/material-ui/src/Popper/Popper.js
--- a/packages/material-ui/src/Popper/Popper.js
+++ b/packages/material-ui/src/Popper/Popper.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import PopperJS from 'popper.js';
 import { chainPropTypes } from '@material-ui/utils';
 import Portal from '../Portal';
+import { setRef, withForwardedRef } from '../utils';
 
 function flipPlacement(placement) {
   const direction = (typeof window !== 'undefined' && document.body.getAttribute('dir')) || 'ltr';
@@ -33,7 +34,7 @@ function getAnchorEl(anchorEl) {
  * Poppers rely on the 3rd party library [Popper.js](https://github.com/FezVrasta/popper.js) for positioning.
  */
 class Popper extends React.Component {
-  tooltip = React.createRef();
+  tooltipRef = React.createRef();
 
   constructor(props) {
     super();
@@ -84,7 +85,7 @@ class Popper extends React.Component {
 
   handleOpen = () => {
     const { anchorEl, modifiers, open, placement, popperOptions = {}, disablePortal } = this.props;
-    const popperNode = this.tooltip.current;
+    const popperNode = this.tooltipRef.current;
 
     if (!popperNode || !anchorEl || !open) {
       return;
@@ -139,12 +140,18 @@ class Popper extends React.Component {
     this.popper = null;
   };
 
+  handleRef = ref => {
+    setRef(this.props.innerRef, ref);
+    setRef(this.tooltipRef, ref);
+  };
+
   render() {
     const {
       anchorEl,
       children,
       container,
       disablePortal,
+      innerRef,
       keepMounted,
       modifiers,
       open,
@@ -173,7 +180,7 @@ class Popper extends React.Component {
     return (
       <Portal onRendered={this.handleOpen} disablePortal={disablePortal} container={container}>
         <div
-          ref={this.tooltip}
+          ref={this.handleRef}
           role="tooltip"
           style={{
             // Prevents scroll issue, waiting for Popper.js to add this style once initiated.
@@ -244,6 +251,11 @@ Popper.propTypes = {
    * The children stay within it's parent DOM hierarchy.
    */
   disablePortal: PropTypes.bool,
+  /**
+   * @ignore
+   * from `withForwardedRef`
+   */
+  innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
   /**
    * Always keep the children in the DOM.
    * This property can be useful in SEO situation or
@@ -297,4 +309,4 @@ Popper.defaultProps = {
   transition: false,
 };
 
-export default Popper;
+export default withForwardedRef(Popper);
diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
--- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
+++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js
@@ -9,6 +9,7 @@ import { duration } from '../styles/transitions';
 import withTheme from '../styles/withTheme';
 import { getTransitionProps } from '../transitions/utils';
 import NoSsr from '../NoSsr';
+import withForwardedRef from '../utils/withForwardedRef';
 import SwipeArea from './SwipeArea';
 
 // This value is closed to what browsers are using internally to
@@ -354,6 +355,7 @@ class SwipeableDrawer extends React.Component {
       disableDiscovery,
       disableSwipeToOpen,
       hysteresis,
+      innerRef,
       minFlingVelocity,
       ModalProps: { BackdropProps, ...ModalPropsProp } = {},
       onOpen,
@@ -387,6 +389,7 @@ class SwipeableDrawer extends React.Component {
             ref: this.handlePaperRef,
           }}
           anchor={anchor}
+          ref={innerRef}
           {...other}
         />
         {!disableSwipeToOpen && variant === 'temporary' && (
@@ -433,6 +436,11 @@ SwipeableDrawer.propTypes = {
    * Specified as percent (0-1) of the width of the drawer
    */
   hysteresis: PropTypes.number,
+  /**
+   * @ignore
+   * from `withForwardedRef`
+   */
+  innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
   /**
    * Defines, from which (average) velocity on, the swipe is
    * defined as complete although hysteresis isn't reached.
@@ -503,4 +511,4 @@ SwipeableDrawer.defaultProps = {
   variant: 'temporary', // Mobile first.
 };
 
-export default withTheme(SwipeableDrawer);
+export default withTheme(withForwardedRef(SwipeableDrawer));


Test patch:
diff --git a/packages/material-ui/src/Collapse/Collapse.test.js b/packages/material-ui/src/Collapse/Collapse.test.js
--- a/packages/material-ui/src/Collapse/Collapse.test.js
+++ b/packages/material-ui/src/Collapse/Collapse.test.js
@@ -7,7 +7,6 @@ import {
   createMount,
   describeConformance,
   getClasses,
-  unwrap,
 } from '@material-ui/core/test-utils';
 import Collapse from './Collapse';
 
@@ -34,13 +33,18 @@ describe('<Collapse />', () => {
     classes,
     inheritComponent: 'Transition',
     mount,
-    skip: ['refForwarding'],
-    testComponentPropWith: false,
+    refInstanceof: window.HTMLDivElement,
+    testComponentPropWith: 'span',
   }));
 
   it('should render a container around the wrapper', () => {
     const wrapper = shallow(<Collapse {...props} classes={{ container: 'woofCollapse1' }} />);
-    const child = new ReactWrapper(wrapper.props().children('entered'));
+    const child = new ReactWrapper(
+      wrapper
+        .dive()
+        .props()
+        .children('entered'),
+    );
     assert.strictEqual(child.name(), 'div');
     assert.strictEqual(child.hasClass(classes.container), true);
     assert.strictEqual(child.hasClass('woofCollapse1'), true);
@@ -49,7 +53,12 @@ describe('<Collapse />', () => {
   it('should render a wrapper around the children', () => {
     const children = <h1>Hello</h1>;
     const wrapper = shallow(<Collapse {...props}>{children}</Collapse>);
-    const child = new ReactWrapper(wrapper.props().children('entered'));
+    const child = new ReactWrapper(
+      wrapper
+        .dive()
+        .props()
+        .children('entered'),
+    );
     assert.strictEqual(child.childAt(0).name(), 'div');
     assert.strictEqual(
       child
@@ -97,7 +106,7 @@ describe('<Collapse />', () => {
           }}
         />,
       );
-      instance = wrapper.instance();
+      instance = wrapper.dive().instance();
       element = { getBoundingClientRect: () => ({}), style: {} };
     });
 
@@ -118,7 +127,7 @@ describe('<Collapse />', () => {
 
     before(() => {
       wrapper = shallow(<Collapse {...props} />);
-      instance = wrapper.instance();
+      instance = wrapper.dive().instance();
     });
 
     describe('handleEnter()', () => {
@@ -151,7 +160,7 @@ describe('<Collapse />', () => {
       it('should call handleEntering', () => {
         const onEnteringStub = spy();
         wrapper.setProps({ onEntering: onEnteringStub });
-        instance = wrapper.instance();
+        instance = wrapper.dive().instance();
         instance.handleEntering(element);
 
         assert.strictEqual(onEnteringStub.callCount, 1);
@@ -168,7 +177,7 @@ describe('<Collapse />', () => {
           restore = theme.transitions.getAutoHeightDuration;
           theme.transitions.getAutoHeightDuration = stub().returns('woofCollapseStub');
           wrapper.setProps({ timeout: 'auto' });
-          instance = wrapper.instance();
+          instance = wrapper.dive().instance();
         });
 
         after(() => {
@@ -197,7 +206,7 @@ describe('<Collapse />', () => {
         it('number should set timeout to ms', () => {
           timeoutMock = 3;
           wrapper.setProps({ timeout: timeoutMock });
-          instance = wrapper.instance();
+          instance = wrapper.dive().instance();
           instance.handleEntering(element);
 
           assert.strictEqual(element.style.transitionDuration, `${timeoutMock}ms`);
@@ -206,7 +215,7 @@ describe('<Collapse />', () => {
         it('nothing should not set timeout', () => {
           const elementBackup = element;
           wrapper.setProps({ timeout: undefined });
-          instance = wrapper.instance();
+          instance = wrapper.dive().instance();
           instance.handleEntering(element);
 
           assert.strictEqual(
@@ -227,7 +236,7 @@ describe('<Collapse />', () => {
         handleEnteredWrapper = shallow(<Collapse {...props} />);
         onEnteredSpy = spy();
         handleEnteredWrapper.setProps({ onEntered: onEnteredSpy });
-        handleEnteredInstance = handleEnteredWrapper.instance();
+        handleEnteredInstance = handleEnteredWrapper.dive().instance();
         element = { style: { height: 666, transitionDuration: '500ms' } };
         handleEnteredInstance.handleEntered(element);
       });
@@ -265,7 +274,7 @@ describe('<Collapse />', () => {
       it('should call onExiting', () => {
         const onExitingStub = spy();
         wrapper.setProps({ onExiting: onExitingStub });
-        instance = wrapper.instance();
+        instance = wrapper.dive().instance();
         instance.handleExiting(element);
 
         assert.strictEqual(onExitingStub.callCount, 1);
@@ -282,7 +291,7 @@ describe('<Collapse />', () => {
           restore = theme.transitions.getAutoHeightDuration;
           theme.transitions.getAutoHeightDuration = stub().returns('woofCollapseStub2');
           wrapper.setProps({ timeout: 'auto' });
-          instance = wrapper.instance();
+          instance = wrapper.dive().instance();
         });
 
         after(() => {
@@ -311,7 +320,7 @@ describe('<Collapse />', () => {
         it('number should set timeout to ms', () => {
           timeoutMock = 3;
           wrapper.setProps({ timeout: timeoutMock });
-          instance = wrapper.instance();
+          instance = wrapper.dive().instance();
           instance.handleExiting(element);
 
           assert.strictEqual(element.style.transitionDuration, `${timeoutMock}ms`);
@@ -320,7 +329,7 @@ describe('<Collapse />', () => {
         it('nothing should not set timeout', () => {
           const elementBackup = element;
           wrapper.setProps({ timeout: undefined });
-          instance = wrapper.instance();
+          instance = wrapper.dive().instance();
           instance.handleExiting(element);
 
           assert.strictEqual(
@@ -346,8 +355,8 @@ describe('<Collapse />', () => {
 
     it('should return autoTransitionDuration when timeout is auto', () => {
       const wrapper = shallow(<Collapse {...props} timeout="auto" />);
-      assert.strictEqual(wrapper.props().timeout, null);
-      instance = wrapper.instance();
+      assert.strictEqual(wrapper.dive().props().timeout, null);
+      instance = wrapper.dive().instance();
       const next = spy();
 
       const autoTransitionDuration = 10;
@@ -368,7 +377,7 @@ describe('<Collapse />', () => {
       const timeout = 10;
       const wrapper = shallow(<Collapse {...props} timeout={timeout} />);
       assert.strictEqual(wrapper.props().timeout, timeout);
-      instance = wrapper.instance();
+      instance = wrapper.dive().instance();
 
       const next = spy();
       instance.addEndListener(null, next);
@@ -382,8 +391,9 @@ describe('<Collapse />', () => {
     let mountInstance;
 
     before(() => {
-      const CollapseNaked = unwrap(Collapse);
-      mountInstance = mount(<CollapseNaked classes={{}} theme={{}} />).instance();
+      mountInstance = mount(<Collapse />)
+        .find('Collapse')
+        .instance();
     });
 
     it('instance should have a wrapper property', () => {
@@ -396,13 +406,18 @@ describe('<Collapse />', () => {
 
     it('should work when closed', () => {
       const wrapper = shallow(<Collapse {...props} collapsedHeight={collapsedHeight} />);
-      const child = new ReactWrapper(wrapper.props().children('entered'));
+      const child = new ReactWrapper(
+        wrapper
+          .dive()
+          .props()
+          .children('entered'),
+      );
       assert.strictEqual(child.props().style.minHeight, collapsedHeight);
     });
 
     it('should be taken into account in handleExiting', () => {
       const wrapper = shallow(<Collapse {...props} collapsedHeight={collapsedHeight} />);
-      const instance = wrapper.instance();
+      const instance = wrapper.dive().instance();
       const element = { style: { height: 666, transitionDuration: undefined } };
       instance.handleExiting(element);
       assert.strictEqual(element.style.height, collapsedHeight, 'should have 0px height');
diff --git a/packages/material-ui/src/Popper/Popper.test.js b/packages/material-ui/src/Popper/Popper.test.js
--- a/packages/material-ui/src/Popper/Popper.test.js
+++ b/packages/material-ui/src/Popper/Popper.test.js
@@ -29,7 +29,7 @@ describe('<Popper />', () => {
     classes: {},
     inheritComponent: 'div',
     mount,
-    refInstanceof: React.Component,
+    refInstanceof: window.HTMLDivElement,
     testComponentPropWith: false,
   }));
 
@@ -51,7 +51,7 @@ describe('<Popper />', () => {
             return null;
           }}
         </Popper>,
-      );
+      ).dive();
       assert.strictEqual(renderSpy.callCount, 1);
       assert.strictEqual(renderSpy.args[0][0], 'top');
     });
@@ -87,7 +87,7 @@ describe('<Popper />', () => {
               return null;
             }}
           </Popper>,
-        );
+        ).dive();
         assert.strictEqual(renderSpy.callCount, 1);
         assert.strictEqual(renderSpy.args[0][0], test.out);
       });
@@ -108,7 +108,7 @@ describe('<Popper />', () => {
 
     it('should position the popper when opening', () => {
       const wrapper = mount(<Popper {...defaultProps} open={false} />);
-      const instance = wrapper.instance();
+      const instance = wrapper.find('Popper').instance();
       assert.strictEqual(instance.popper == null, true);
       wrapper.setProps({ open: true });
       assert.strictEqual(instance.popper !== null, true);
@@ -116,7 +116,7 @@ describe('<Popper />', () => {
 
     it('should not position the popper when closing', () => {
       const wrapper = mount(<Popper {...defaultProps} open />);
-      const instance = wrapper.instance();
+      const instance = wrapper.find('Popper').instance();
       assert.strictEqual(instance.popper !== null, true);
       wrapper.setProps({ open: false });
       assert.strictEqual(instance.popper, null);
@@ -134,7 +134,7 @@ describe('<Popper />', () => {
           )}
         </Popper>,
       );
-      const instance = wrapper.instance();
+      const instance = wrapper.find('Popper').instance();
       assert.strictEqual(wrapper.find('span').length, 1);
       assert.strictEqual(wrapper.find('span').text(), 'Hello World');
       assert.strictEqual(instance.popper !== null, true);
@@ -165,7 +165,7 @@ describe('<Popper />', () => {
         .find(Grow)
         .props()
         .onExited();
-      assert.strictEqual(wrapper.state().exited, true);
+      assert.strictEqual(wrapper.find('Popper').instance().state.exited, true);
     });
   });
 
diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js
--- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js
+++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js
@@ -62,7 +62,7 @@ describe('<SwipeableDrawer />', () => {
     classes: {},
     inheritComponent: Drawer,
     mount,
-    refInstanceof: React.Component,
+    refInstanceof: window.HTMLDivElement,
     testComponentPropWith: false,
   }));
 
@@ -74,7 +74,7 @@ describe('<SwipeableDrawer />', () => {
         open={false}
         theme={createMuiTheme()}
       />,
-    );
+    ).find('SwipeableDrawer'); // unwrap withForwardedRef
     assert.strictEqual(wrapper.childAt(0).type(), Drawer);
     assert.strictEqual(
       wrapper
@@ -83,7 +83,6 @@ describe('<SwipeableDrawer />', () => {
         .type(),
       SwipeArea,
     );
-    wrapper.unmount();
   });
 
   it('should hide the SwipeArea if swipe to open is disabled', () => {
@@ -95,9 +94,8 @@ describe('<SwipeableDrawer />', () => {
         theme={createMuiTheme()}
         disableSwipeToOpen
       />,
-    );
+    ).find('SwipeableDrawer');
     assert.strictEqual(wrapper.children().length, 1);
-    wrapper.unmount();
   });
 
   it('should accept user custom style', () => {
@@ -110,7 +108,7 @@ describe('<SwipeableDrawer />', () => {
         theme={createMuiTheme()}
         PaperProps={customStyle}
       />,
-    );
+    ).find('SwipeableDrawer');
 
     assert.strictEqual(wrapper.props().PaperProps, customStyle);
   });
@@ -130,7 +128,7 @@ describe('<SwipeableDrawer />', () => {
           <h1>Hello</h1>
         </SwipeableDrawerNaked>,
       );
-      instance = wrapper.instance();
+      instance = wrapper.find('SwipeableDrawer').instance();
       mockDrawerDOMNode(wrapper);
     });
 
@@ -220,7 +218,7 @@ describe('<SwipeableDrawer />', () => {
           const handleOpen = spy();
           wrapper.setProps({ onOpen: handleOpen });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.openTouches[0]] });
-          assert.strictEqual(wrapper.state().maybeSwiping, true);
+          assert.strictEqual(instance.state.maybeSwiping, true);
           fireBodyMouseEvent('touchmove', { touches: [params.openTouches[1]] });
           assert.strictEqual(instance.isSwiping, true);
           fireBodyMouseEvent('touchmove', { touches: [params.openTouches[2]] });
@@ -233,7 +231,7 @@ describe('<SwipeableDrawer />', () => {
           const handleClose = spy();
           wrapper.setProps({ open: true, onClose: handleClose });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.closeTouches[0]] });
-          assert.strictEqual(wrapper.state().maybeSwiping, true);
+          assert.strictEqual(instance.state.maybeSwiping, true);
           fireBodyMouseEvent('touchmove', { touches: [params.closeTouches[1]] });
           assert.strictEqual(instance.isSwiping, true);
           fireBodyMouseEvent('touchmove', { touches: [params.closeTouches[2]] });
@@ -247,7 +245,7 @@ describe('<SwipeableDrawer />', () => {
           const handleOpen = spy();
           wrapper.setProps({ onOpen: handleOpen });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.openTouches[0]] });
-          assert.strictEqual(wrapper.state().maybeSwiping, true);
+          assert.strictEqual(instance.state.maybeSwiping, true);
           fireBodyMouseEvent('touchmove', { touches: [params.openTouches[1]] });
           assert.strictEqual(instance.isSwiping, true);
           fireBodyMouseEvent('touchend', { changedTouches: [params.openTouches[1]] });
@@ -259,7 +257,7 @@ describe('<SwipeableDrawer />', () => {
           const handleClose = spy();
           wrapper.setProps({ open: true, onClose: handleClose });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.closeTouches[0]] });
-          assert.strictEqual(wrapper.state().maybeSwiping, true);
+          assert.strictEqual(instance.state.maybeSwiping, true);
           fireBodyMouseEvent('touchmove', { touches: [params.closeTouches[1]] });
           assert.strictEqual(instance.isSwiping, true);
           fireBodyMouseEvent('touchend', { changedTouches: [params.closeTouches[1]] });
@@ -300,7 +298,7 @@ describe('<SwipeableDrawer />', () => {
           const handleClose = spy();
           wrapper.setProps({ onOpen: handleOpen, onClose: handleClose });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.edgeTouch] });
-          assert.strictEqual(wrapper.state().maybeSwiping, true);
+          assert.strictEqual(instance.state.maybeSwiping, true);
           assert.strictEqual(instance.setPosition.callCount, 1);
           fireBodyMouseEvent('touchend', { changedTouches: [params.edgeTouch] });
           assert.strictEqual(handleOpen.callCount, 0);
@@ -319,7 +317,7 @@ describe('<SwipeableDrawer />', () => {
             onClose: handleClose,
           });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.edgeTouch] });
-          assert.strictEqual(wrapper.state().maybeSwiping, true);
+          assert.strictEqual(instance.state.maybeSwiping, true);
           assert.strictEqual(instance.setPosition.callCount, 1);
           fireBodyMouseEvent('touchend', { changedTouches: [params.edgeTouch] });
           assert.strictEqual(handleOpen.callCount, 0);
@@ -339,7 +337,7 @@ describe('<SwipeableDrawer />', () => {
             onClose: handleClose,
           });
           fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.ignoreTouch] });
-          assert.strictEqual(wrapper.state().maybeSwiping, false);
+          assert.strictEqual(instance.state.maybeSwiping, false);
           assert.strictEqual(instance.setPosition.callCount, 0);
           fireBodyMouseEvent('touchend', { changedTouches: [params.ignoreTouch] });
           assert.strictEqual(handleOpen.callCount, 0);
@@ -357,11 +355,11 @@ describe('<SwipeableDrawer />', () => {
       assert.strictEqual(instance.isSwiping, null);
       fireBodyMouseEvent('touchmove', { touches: [{ pageX: 10, clientY: 0 }] });
       assert.strictEqual(instance.isSwiping, true);
-      assert.strictEqual(wrapper.state().maybeSwiping, true);
+      assert.strictEqual(instance.state.maybeSwiping, true);
       wrapper.setProps({
         open: false,
       });
-      assert.strictEqual(wrapper.state().maybeSwiping, false);
+      assert.strictEqual(instance.state.maybeSwiping, false);
     });
 
     it('should wait for a clear signal to determine this.isSwiping', () => {
@@ -412,7 +410,7 @@ describe('<SwipeableDrawer />', () => {
       wrapper.setProps({ disableSwipeToOpen: true });
       fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 0, clientY: 0 }] });
       assert.strictEqual(
-        wrapper.state().maybeSwiping,
+        wrapper.find('SwipeableDrawer').instance().state.maybeSwiping,
         false,
         'should not be listening for open swipe',
       );
@@ -435,7 +433,7 @@ describe('<SwipeableDrawer />', () => {
       wrapper.setProps({ disableSwipeToOpen: true, open: true });
       fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] });
       assert.strictEqual(
-        wrapper.state().maybeSwiping,
+        wrapper.find('SwipeableDrawer').instance().state.maybeSwiping,
         true,
         'should not be listening for open swipe',
       );
@@ -494,7 +492,10 @@ describe('<SwipeableDrawer />', () => {
     );
     fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 0, clientY: 0 }] });
     // simulate paper ref being null because of the drawer being updated
-    wrapper.instance().handlePaperRef(null);
+    wrapper
+      .find('SwipeableDrawer')
+      .instance()
+      .handlePaperRef(null);
     fireBodyMouseEvent('touchmove', { touches: [{ pageX: 20, clientY: 0 }] });
   });
 
diff --git a/packages/material-ui/src/Tooltip/Tooltip.test.js b/packages/material-ui/src/Tooltip/Tooltip.test.js
--- a/packages/material-ui/src/Tooltip/Tooltip.test.js
+++ b/packages/material-ui/src/Tooltip/Tooltip.test.js
@@ -8,6 +8,7 @@ import Popper from '../Popper';
 import Tooltip from './Tooltip';
 import Input from '../Input';
 import createMuiTheme from '../styles/createMuiTheme';
+import RootRef from '../RootRef';
 
 function persist() {}
 
@@ -40,8 +41,8 @@ describe('<Tooltip />', () => {
   it('should render the correct structure', () => {
     const wrapper = shallow(<Tooltip {...defaultProps} />);
     assert.strictEqual(wrapper.type(), React.Fragment);
-    assert.strictEqual(wrapper.childAt(0).name(), 'RootRef');
-    assert.strictEqual(wrapper.childAt(1).name(), 'Popper');
+    assert.strictEqual(wrapper.childAt(0).type(), RootRef);
+    assert.strictEqual(wrapper.childAt(1).type(), Popper);
     assert.strictEqual(wrapper.childAt(1).hasClass(classes.popper), true);
   });