react-native-side-menu
react-native-side-menu copied to clipboard
Close side menu clicking on another button
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.....
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
@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 https://github.com/react-native-community/react-native-side-menu/pull/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.
@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:
Any ideas where this is going wrong? Why the menu isn't opening as soon as isOpen
has its state updated?