Skip to content
This repository has been archived by the owner on Sep 13, 2020. It is now read-only.

Close side menu clicking on another button #245

Open
IronTony opened this issue Sep 24, 2016 · 3 comments
Open

Close side menu clicking on another button #245

IronTony opened this issue Sep 24, 2016 · 3 comments

Comments

@IronTony
Copy link

IronTony commented Sep 24, 2016

Hi all,

I have read these issues, but I didn't find an answer to my question.
I am using this component, that made my day, and I am using this with Router Flux.
So in my App.js as you can see I have the sidemenu and the router:

<ScrollView>
   <SideMenu
    menu={menu}
    isOpen={this.state.isOpen}
    onChange={(isOpen) => this.updateMenuState(isOpen)}
    menuPosition={'right'}
    scrollsToTop={false}
    openMenuOffset={Dimensions.get('window').width * 0.85}
    > 
 <View style={{marginTop:20, height: 40, backgroundColor: 'white'}}>
      <Button style={styles.button} onPress={() => this.toggle()}>
        <Icon name="menu" />
      </Button>
    </View>
    <View style={styles.containerNoPad}>
      <Router hideNavBar={true}>
        <Scene key='root'>
          <Scene key='homepage' component={HomePage} title='Homepage' initial={true} />
          ..........
        </Scene>
      </Router>
    </View>
  </SideMenu>
</ScrollView>

Then in Menu.js I have the render() of the menu. I'd like that clicking on a menu item, the menu will be closed automatically with its effect. Unfortunately I am not able to do this. Any help?

This is my menu item:

<View>
  <Button onPress={() =>  {
      Actions.anotherPage({hideNavBar: true, type: 'reset'})
    }}>
    <View>
      <Icon name="paging" />
    </View>
    <View>
      <Text>Another page</Text>
    </View>
  </Button>
</View>

Sorry for the indentation problem.....

@Kureev
Copy link
Owner

Kureev commented Sep 26, 2016

Hi @IronTony,

Unfortunately, I don't have enough time to make an example for you, but the general idea would be like this:

When you click on button, emit an action that re-render menu with two props:

  • isOpen={false} to close the menu
  • onChange={processPendingTransition} in order to process your pending transition to the next screen

@robhogan
Copy link

@Kureev - unfortunately if you want to maintain your state when also using gestures you have to dispatch an action from onChange too, and since changing isOpen causes onChange to be fired, you end up with two actions and possibly an unnecessary render in that case.

It's tricky to work around because onChange is fired before the parent has received the new isOpen state (eg from redux/flux), so it's hard* to detect from the onChange handler whether we're responding to a swipe and should dispatch an action, or responding to props-driven change and have already dispatched an action.

This seemed to be what @grabbou addressed in #175 addressed but that's a while out of date now.

* componentWillReceiveProps in the parent provides a hacky workaround - assuming your parent is also props driven, the new prop will appear hear and you can compare with onChange to decide whether to dispatch an action.

@tetreault
Copy link

@Kureev @rh389 I have an issue with what you said which is related to this. Hopefully you might be able to help, thank you for your time! If you don't mind, allow me to provide a verbose version so I'm clear.

I'm using react-native-side-menu and react-native-hamburger. the side menu is in a file called Nav.js, the hamburger button is used in a separate view, and is in its own file called HamburgerButton.js. I set up a simple observer pattern with the corresponding Actions.js, ActionTypes.js, Dispatcher.js, and Store.js.

I can confirm if I press the hamburger component located inside the homescreen view, it dispatches an event to update the state of "menuState" to true, the same event is listened to by Nav.js which then updates the side menu's isOpen prop. Both states react and update correctly. However, the side menu does not open when its state gets updated.

If you don't mind, here's the source code:

HamburgerButton.js:

'use strict';

import Expo from 'expo';
import React from 'react';
import {
  StyleSheet
} from 'react-native';
import Hamburger from 'react-native-hamburger';
import Actions from '../actions/Actions';
import ActionTypes from '../actions/ActionTypes';
import Store from '../store/Store';

export default class MenuButton extends React.Component {
  constructor(props) {
    super(props);
    // get initial state from Store
    this.state = {
      active: Store.getStore().menuState
    };
    this._onPress = this._onPress.bind(this);
    this._changeState = this._changeState.bind(this);
    console.log('Hamburger this', `${JSON.stringify(this)}`);
  }

  // state changed, get updated state from store
  _changeState() {
    this.state = {
      active: Store.getStore().menuState
    };
    console.log('Hamburger State Updated', `${JSON.stringify(this.state.active)}`);
  }

  // menu icon was pressed, fire action to update state
  _onPress() {
    console.log('Hamburger _onPress called');
    Actions.toggleMenu(!this.state.active);
  }

  // mount event listener that will talk to store and trigger this._changeState
  componentDidMount() {
    console.log('Hamburger componentDidMount called');
    Store.addChangeListener(this._changeState);
  }

  render() {
    return (
      <Hamburger
        active={this.props.active}
        type='spinCross'
        onPress={this._onPress} />
    );
  }
}

Nav.js (has the side menu):

'use strict';

import Expo from 'expo';
import React from 'react';
import {
  SideMenu
} from 'react-native-elements';
import {
  StyleSheet,
  View,
  Text,
  App,
  Dimensions
} from 'react-native';
import HomeScreen from './HomeScreen';
import MenuContent from '../components/MenuContent';
import Actions from '../actions/Actions';
import ActionTypes from '../actions/ActionTypes';
import Store from '../store/Store';

// dimensions
const SCREEN_WIDTH = Dimensions.get('window').width;

export default class Nav extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      isOpen: Store.getStore().menuState
    };
    this._changeState = this._changeState.bind(this);
    this._onChange = this._onChange.bind(this);
    console.log('Nav this', `${JSON.stringify(this)}`);
  };

  // state changed, get updated state from store
  _changeState() {
    this.state = {
      isOpen: Store.getStore().menuState
    };
    console.log('Nav State Updated', `${JSON.stringify(this.state.isOpen)}`);
  }

  // menu has changed, fire action to update state
  _onChange () {
    console.log('Nav _onChange Called');
    Actions.toggleMenu(!this.state.isOpen);
  }

  // mount event listener that will talk to store and trigger this._changeState
  componentDidMount() {
    console.log('Nav componentDidMount called');
    Store.addChangeListener(this._changeState);
  }

  render () {
    const MenuComponent = (
      <View style={styles.menuView}>
        <MenuContent></MenuContent>
      </View>
    );

    return (
      <SideMenu
        ref='menu'
        isOpen={this.state.isOpen}
        onChange={this._onChange}
        menuPosition='right'
        openMenuOffset={SCREEN_WIDTH * 0.85}
        menu={MenuComponent}>
        <HomeScreen
          style={styles.HomeScreen}
        />
      </SideMenu>
    );
  }
}

const styles = StyleSheet.create({
  menuView: {
    flex: 1,
    backgroundColor: '#ededed',
    paddingTop: 20
  }
});

The console logs show everything happening correctly:
screenshot 2017-05-17 14 01 36

Any ideas where this is going wrong? Why the menu isn't opening as soon as isOpen has its state updated?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants