React-native-navigation: v2 Android Side Menu Unexpected Behavior

Created on 6 Feb 2019  路  13Comments  路  Source: wix/react-native-navigation

Issue Description

There are two different scenarios that I have found where tapping outside of an open side menu to close, or swiping the side menu open after setting the side menu visible option to false results in unexpected behavior.

My overall question is what is happening on the swiping gesture and when you tap outside of an open side menu? It seems like it opens and closes the side menu as expected, but it doesn't update the screen options, so if visible is set to false I am unable to open the side menu with the swiping gesture. And if visible is set to true and I tap outside of the side menu to close it, it will close it, but then anytime I navigate to a screen and then back the side menu thinks it is open because the screen options don't seem to get updated on those tap outside to close/swipe to open gestures.

Steps to Reproduce / Code Snippets / Screenshots

Below is the mergeOptions function I am using to set visible to true/false in order to show or hide the side menu when tapping on the menu icon, or on one of the links in the menu.

const setSideMenuVisibility = (componentId, isVisible) => {
    Navigation.mergeOptions(componentId, {
        sideMenu: {
            left: {
                visible: isVisible
            }
        }
    });
}

Scenario 1

  1. Open the side menu using mergeOptions and setting visible to true
  2. Tap on the outside area to close the side menu (you should see the side menu close)
  3. Push another screen onto the navigation stack
  4. Use the top bar button to go back to the original screen
  5. The side menu will be open.

As you can see below, when I tap on the menu icon, it opens the side menu by setting visible to true, then I tap outside of the side menu to close it, then I tap on the button to push screen 2 onto my navigation stack, then I tap the topbar button to go back, then I'm back on screen 1, but the side menu is open.
side menu stays open when going back

Scenario 2

  1. Open the side menu using mergeOptions and setting visible to true
  2. Tap on a link in the side menu (this is where I call mergeOptions and set the side menu visible option to false, then push the new screen onto my navigation stack)
  3. Use the topbar button to go back to the original screen
  4. Try to swipe open the side menu and you will see it close right away.

As you can see below, when I tap on the menu icon, it opens the side menu by setting visible to true, then I tap on the Screen 2 link which sets visible to false using mergeOptions, then pushes screen 2 onto the navigation stack, then I am able to successfully swipe open the side menu as expected, but when I tap on the back button on the top bar and go back to the original screen, I am not able to swipe open the side menu, it just closes right away.
cannot swipe open side menu


Environment

  • React Native Navigation version: 2.7.1
  • React Native version: 0.57.8
  • Platform(s) (iOS, Android, or both?): Android
  • Device info (Simulator/Device? OS version? Debug/Release?): Tested on Galaxy S6 running Android 7.0. Also tested on a Pixel2 XL simulator running Android 8.1. It occurs on both Debug and Release builds.
Android acceptebug

Most helpful comment

Hi @Collin3, I just sent a PR that addresses this issue. If it does indeed fix your issue, do not forget to close the issue!

All 13 comments

Scenario 2 is similar to #4636

@guyca
I'm also dealing with the same problem,

In addition, on opening/closing side menu, appear/disappear events don't get dispatched

Navigation.events().registerComponentDidAppearListener(({componentId}) => {
// does not works for side menu in android
});

and also if I put componentDidAppear __method__ inside side menu component class,
only fires on @Collin3 __Scenario 1__ situation, when screen got popped and side menu opens automatically!

Update:
__This happens when I use stack for sideMenu__

{
  "left": {
    "stack": {
      "children": [
        {
          "component": {
            "name": "pharma.sideMenu.right"
          }
        }
      ],
      "id": "pharma.left",
      "options": {
        "topBar": {
          "visible": false,
          "drawBehind": true
        }
      }
    }
  },
  "center": {
    "stack": {
      "children": [
        {
          "component": {
            "name": "pharma.home",
            "options": {
              "topBar": {
                "title": {
                  "text": "Home"
                },

              }
            }
          }
        }
      ],
      "id": "pharma"
    }
  }
}

@guyca
And also another issue is if _Side Menu_ got opened using mergeOptions,
then try to reload application <R,R>, app will crash due.

java.lang.RuntimeException: Tried to create view after it has already been destroyed O

__Stack Trace__

com.reactnativenavigation.viewcontrollers.ViewController.getView ViewController.java:161
com.reactnativenavigation.viewcontrollers.ParentController.getView ParentController.java:60
com.reactnativenavigation.viewcontrollers.ChildController.onViewBroughtToFront ChildController.java:45
com.reactnativenavigation.viewcontrollers.ChildControllersRegistry.onViewDisappear ChildControllersRegistry.java:15
com.reactnativenavigation.viewcontrollers.ChildController.onViewDisappear ChildController.java:41
com.reactnativenavigation.viewcontrollers.ComponentViewController.onViewDisappear ComponentViewController.java:48
com.reactnativenavigation.viewcontrollers.ViewController.destroy ViewController.java:231
com.reactnativenavigation.viewcontrollers.ChildController.destroy ChildController.java:70
com.reactnativenavigation.viewcontrollers.ComponentViewController.destroy ComponentViewController.java:93
com.reactnativenavigation.viewcontrollers.ParentController.destroy ParentController.java:112
com.reactnativenavigation.viewcontrollers.stack.StackController.destroy StackController.java:138
com.reactnativenavigation.viewcontrollers.ParentController.destroy ParentController.java:112
com.reactnativenavigation.viewcontrollers.navigator.Navigator.destroyRoot Navigator.java:121
com.reactnativenavigation.viewcontrollers.navigator.Navigator.destroyViews Navigator.java:117
com.reactnativenavigation.NavigationActivity.onReload NavigationActivity.java:124
com.reactnativenavigation.react.JsDevReloadHandler.reloadReactNative JsDevReloadHandler.java:83
com.reactnativenavigation.react.JsDevReloadHandler.onKeyUp JsDevReloadHandler.java:70
com.reactnativenavigation.react.ReactGateway.onKeyUp ReactGateway.java:68
com.reactnativenavigation.NavigationActivity.onKeyUp NavigationActivity.java:93
android.view.KeyEvent.dispatch KeyEvent.java:2715
android.support.v4.view.KeyEventDispatcher.activitySuperDispatchKeyEventPre28 KeyEventDispatcher.java:137
android.support.v4.view.KeyEventDispatcher.dispatchKeyEvent KeyEventDispatcher.java:87
android.support.v4.app.SupportActivity.dispatchKeyEvent ComponentActivity.java:126
android.support.v7.app.AppCompatActivity.dispatchKeyEvent AppCompatActivity.java:535
android.support.v7.view.WindowCallbackWrapper.dispatchKeyEvent WindowCallbackWrapper.java:59
android.support.v7.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent AppCompatDelegateImpl.java:2533
com.android.internal.policy.DecorView.dispatchKeyEvent DecorView.java:354
android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent ViewRootImpl.java:4733
android.view.ViewRootImpl$ViewPostImeInputStage.onProcess ViewRootImpl.java:4605
android.view.ViewRootImpl$InputStage.deliver ViewRootImpl.java:4147
android.view.ViewRootImpl$InputStage.onDeliverToNext ViewRootImpl.java:4200
android.view.ViewRootImpl$InputStage.forward ViewRootImpl.java:4166
android.view.ViewRootImpl$AsyncInputStage.forward ViewRootImpl.java:4293
android.view.ViewRootImpl$InputStage.apply ViewRootImpl.java:4174
android.view.ViewRootImpl$AsyncInputStage.apply ViewRootImpl.java:4350
android.view.ViewRootImpl$InputStage.deliver ViewRootImpl.java:4147
android.view.ViewRootImpl$InputStage.onDeliverToNext ViewRootImpl.java:4200
android.view.ViewRootImpl$InputStage.forward ViewRootImpl.java:4166
android.view.ViewRootImpl$InputStage.apply ViewRootImpl.java:4174
android.view.ViewRootImpl$InputStage.deliver ViewRootImpl.java:4147
android.view.ViewRootImpl$InputStage.onDeliverToNext ViewRootImpl.java:4200
android.view.ViewRootImpl$InputStage.forward ViewRootImpl.java:4166
android.view.ViewRootImpl$AsyncInputStage.forward ViewRootImpl.java:4326
android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent ViewRootImpl.java:4487
android.view.inputmethod.InputMethodManager$PendingEvent.run InputMethodManager.java:2435
android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback InputMethodManager.java:1998
android.view.inputmethod.InputMethodManager.finishedInputEvent InputMethodManager.java:1989
android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished InputMethodManager.java:2412
android.view.InputEventSender.dispatchInputEventFinished InputEventSender.java:141
android.os.MessageQueue.nativePollOnce MessageQueue.java
android.os.MessageQueue.next MessageQueue.java:325
android.os.Looper.loop Looper.java:142
android.app.ActivityThread.main ActivityThread.java:6494
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:438
com.android.internal.os.ZygoteInit.main ZygoteInit.java:807

... but its works perfectly when opened via swipe gesture.

Just tested with 2.12.0-snapshot.211 and the bug is still not fixed!
seems like https://github.com/wix/react-native-navigation/pull/4745 didn't help.

I had the same issue and as a workaround I made a function that is handing the merge

updateNavigationState(){ Navigation.mergeOptions(this.props.componentId, { sideMenu: { left: { visible: THECURRENTSTATE } } }); }
The above function is called when

Navigation.events().registerComponentDidDisappearListener(({ componentId }) => { ... this.updateNavigationState(); });

and when

navigationButtonPressed({buttonId}) { ... this.updateNavigationState(); }

As the actual state of the drawer is set when the screens are switched the issue does not appear.

@lbudakov Thanks for sharing your solution. 馃檶
It helped.

I am facing the same issues. Can you, @lbudakov, give a better description of your workaround? Where is the code placed, in the main component (screen) or sidemenu?

@bobmulder it's in the main component
example:
````
class NAME extends Component {

constructor(){
  super();
  this.sideDrawerVisible = false;
}

componentDidMount() {
  this.navigationEventListener = Navigation.events().bindComponent(this);
  Navigation.events().registerComponentDidDisappearListener(({ componentId }) => {
    if (componentId === 'DrawerID') {
      this.sideDrawerVisible = false;
    }
    this.updateNavigationState();
  });
}

navigationButtonPressed({buttonId}) {
  if( buttonId == 'DrawerToggleID' ){
    (!this.sideDrawerVisible) ? this.sideDrawerVisible = true : this.sideDrawerVisible = false;
    this.updateNavigationState();
  }
}

updateNavigationState(){
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
left: {
visible: this.sideDrawerVisible
}
}
});
}

render(){
return(...)
}
}
````

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest Detox and report back. Thank you for your contributions.

There are workarounds, but I鈥檇 still consider this a bug. Not stale in my opinion.

Hi @Collin3, I just sent a PR that addresses this issue. If it does indeed fix your issue, do not forget to close the issue!

@ItsNoHax thanks for the PR! I pulled those changes in to test it out and found that it does indeed fix my issue. I'll be sure to close this once it gets merged in.

Thanks again!

Just Close the Sidemenu before pushing the new screen and give setTimeout to 200 millisecond works. Its not Perfect solution just for workaround.

Navigation.mergeOptions(componentId, {
sideMenu: {
right: {
visible:false
}
}
});

setTimeout(()=>{
Navigation.push(componentId, {
component: {
name: screenNAme,
passProps,
options: {
sideMenu: {
right: {
visible: false,
enabled: false
}
},
bottomTabs: {
visible: bottomTabVisible,
drawBehind: true
},
layout: {
orientation: ["portrait"]
}
}
}
});
},200)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nbolender picture nbolender  路  3Comments

yayanartha picture yayanartha  路  3Comments

swingywc picture swingywc  路  3Comments

henrikra picture henrikra  路  3Comments

zhanguangao picture zhanguangao  路  3Comments