Repository: mui/material-ui (TypeScript)
Date: 2018-04-03 22:18:20+00:00
Task type: Bug Fix

Issue:
[SwipeableDrawer] Drawer crashes if setState occurs while it's opening
- [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
SwipeableDrawer opening swipe should work whether or not a setState is occurring in background.

## Current Behavior
SwipeableDrawer crashes if setState occurs in background while it's opening

## Steps to Reproduce (for bugs)

https://codesandbox.io/s/2w9kw80kvp

1. Open sandbox output in a new window
2. Toggle device toolbar in devtools
3. Open drawer

## Context

I was creating a live example for a [non-related issue](https://github.com/mui-org/material-ui/issues/10862). 

## Your Environment

| Tech         | Version |
|--------------|---------|
| Material-UI  | 1.0.0-beta.39        |
| React        | 16.2.0        |
| browser      | Chrome 65        |



Issue discussion:
I found the cause of this error, but my fix is kind of hacky. I'd like to fix the root issue instead.

@oliviertassinari It seems that the paper and backdrop refs (at least the paper ref) are set multiple times during the life time of the `SwipeableDrawer`. :confused: When the root component updates, as in the codesandbox, it causes the paper ref to become re-mounted, so it is `null` for a short moment (in which the drawer crashes if any event handler is called).

I have a unit test that reproduces this issue. :tada: 

Solutions:

* (hacky) Add `if (!this.paper) return;` to `handleBodyTouchMove` and call it a day
* (preferred) Don't re-mount the paper :open_mouth: 

Solution patch:
diff --git a/src/SwipeableDrawer/SwipeableDrawer.js b/src/SwipeableDrawer/SwipeableDrawer.js
--- a/src/SwipeableDrawer/SwipeableDrawer.js
+++ b/src/SwipeableDrawer/SwipeableDrawer.js
@@ -153,7 +153,8 @@ class SwipeableDrawer extends React.Component {
     this.startY = currentY;
 
     this.setState({ maybeSwiping: true });
-    if (!open) {
+    if (!open && this.paper) {
+      // the ref may be null when a parent component updates while swiping
       this.setPosition(this.getMaxTranslate() + (disableDiscovery ? 20 : -swipeAreaWidth), {
         changeTransition: false,
       });
@@ -166,6 +167,9 @@ class SwipeableDrawer extends React.Component {
   };
 
   handleBodyTouchMove = event => {
+    // the ref may be null when a parent component updates while swiping
+    if (!this.paper) return;
+
     const anchor = getAnchor(this.props);
     const horizontalSwipe = isHorizontal(this.props);
 


Test patch:
diff --git a/src/SwipeableDrawer/SwipeableDrawer.test.js b/src/SwipeableDrawer/SwipeableDrawer.test.js
--- a/src/SwipeableDrawer/SwipeableDrawer.test.js
+++ b/src/SwipeableDrawer/SwipeableDrawer.test.js
@@ -348,4 +348,21 @@ describe('<SwipeableDrawer />', () => {
       wrapper.unmount();
     });
   });
+
+  it('does not crash when updating the parent component while swiping', () => {
+    const wrapper = mount(
+      <SwipeableDrawerNaked
+        onOpen={() => {}}
+        onClose={() => {}}
+        open={false}
+        theme={createMuiTheme()}
+      >
+        <h1>Hello</h1>
+      </SwipeableDrawerNaked>,
+    );
+    fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] });
+    // simulate paper ref being null because of the drawer being updated
+    wrapper.instance().handlePaperRef(null);
+    fireBodyMouseEvent('touchmove', { touches: [{ pageX: 20, clientY: 0 }] });
+  });
 });