Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Full working example of react-native-side-menu with routes or navigation [closed]

I found few posts that give some "hints" on how to integrate navigation\routes with react-native-side-menu, unfortunately didn't find any post that show full working example of such functionality.

I'm also not sure what is the simplest implementation for navigation\routes, and what's the difference between these 2 options (of course this is not specifically to side menu, but in my case should join together).

Can any one point to such example ?

like image 550
Tamir Avatar asked Apr 21 '16 13:04

Tamir


People also ask

Which function is used to configure the navigation in React Native applications?

The createStackNavigator function passes behind the scenes, a navigate prop to the HomeScreen and AboutScreen components. The navigate prop allows for navigation to a specified screen component. This is why we are able to use it on a button at HomeScreen.

What React Native is used for?

React Native is an open-source JavaScript framework, designed for building apps on multiple platforms like iOS, Android, and also web applications, utilizing the very same code base. It is based on React, and it brings all its glory to mobile app development.


3 Answers

If you mean the drawer menu, check react-native-material-design and the given demo-app.

like image 40
duketwo Avatar answered Oct 06 '22 07:10

duketwo


So eventually I was able to integrate standard Navigator as demonstrated in Facebook's official sample code:

https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/Navigator

With react-native-side-menu component, and this is how the entire code looks like:

'use strict';

var React = require('react');
var ReactNative = require('react-native');
var SideMenu = require('react-native-side-menu');

var {
  Component,
  Navigator,
  AppRegistry,
  View,
  Text,
  Image,
  StyleSheet,
  ScrollView,
  TouchableOpacity
} = ReactNative;

class FirstPage extends Component {
    render() {
      return (
        <View style={styles.page}><Text style={styles.pageContent}>First Page</Text></View>
      );    
  }
}

class FirstPageMenu extends Component {

    constructor(props) {
        super(props);
        this.state = {};
    }

    toggle() {
        this.setState({
          isOpen: !this.state.isOpen,
        });
    }

    updateMenuState(isOpen) {
        this.setState({ isOpen, });
    }

    onMenuItemSelected = (item) => {
        this.setState({
            isOpen: false,      
            selectedItem: item,
        });
        this.props.navigator.replace({ id: item });
    }

    render() {

        const menu = <Menu onItemSelected={this.onMenuItemSelected} navigator={this.props.navigator}/>;

        return (
            <SideMenu
              menu={menu}
              isOpen={this.state.isOpen}
              onChange={(isOpen) => this.updateMenuState(isOpen)}>
                <MenuButton onPress={() => this.toggle()}/>
                <FirstPage/>
            </SideMenu>                
        );
    }
};

class SecondPage extends Component {
     ...
}

class SecondPageMenu extends Component {
     ...
}

class ThirdPage extends Component {
     ...
}

class ThirdPageMenu extends Component {
     ...
}

class MenuNavigator extends Component {

  constructor(props) {
    super(props);
    this._setNavigatorRef = this._setNavigatorRef.bind(this);
  }

  renderScene(route, nav) {
    switch (route.id) {
      case 'first':
        return <FirstPageMenu navigator={nav} />;
      case 'second':
        return <SecondPageMenu navigator={nav} />;
      case 'third':
        return <ThirdPageMenu navigator={nav} />;
      default:
        return <FirstPageMenu navigator={nav} />;
    }
  }

  render() {
    return (
      <Navigator
        ref={this._setNavigatorRef}
        initialRoute={{id: 'first'}}
        renderScene={this.renderScene}
        configureScene={(route) => {
          if (route.sceneConfig) {
            return route.sceneConfig;
          }
          return Navigator.SceneConfigs.FloatFromBottom;
        }}
      />
    );
  }

  componentWillUnmount() {
    this._listeners && this._listeners.forEach(listener => listener.remove());
  }

  _setNavigatorRef(navigator) {
    if (navigator !== this._navigator) {
      this._navigator = navigator;

      if (navigator) {
        var callback = (event) => {
          console.log(
            `NavigatorMenu: event ${event.type}`,
            {
              route: JSON.stringify(event.data.route),
              target: event.target,
              type: event.type,
            }
          );
        };
        // Observe focus change events from the owner.
        this._listeners = [
          navigator.navigationContext.addListener('willfocus', callback),
          navigator.navigationContext.addListener('didfocus', callback),
        ];
      }
    }
  }
};

class MenuButton extends Component {

  handlePress(e) {
    if (this.props.onPress) {
      this.props.onPress(e);
    }
  }

  render() {
    return (
      <View style={styles.menuButton} >
        <TouchableOpacity 
          onPress={this.handlePress.bind(this)}
          style={this.props.style}>
          <Text>{this.props.children}</Text>
          <Image
            source={{ uri: 'http://i.imgur.com/vKRaKDX.png', width: 40, height: 40, }} />        
        </TouchableOpacity>      
      </View>
    );
  }
}

class Menu extends Component {

  static propTypes = {
    onItemSelected: React.PropTypes.func.isRequired,
  };

  constructor(props) {
      super(props);
  }

  render() {

    return (

      <ScrollView scrollsToTop={false} style={styles.menu}>

        <Text
          onPress={() => this.props.onItemSelected('first')}
          style={styles.item}>
          First
        </Text>

        <Text
          onPress={() => this.props.onItemSelected('second')}
          style={styles.item}>
          Second
        </Text>

        <Text
          onPress={() => this.props.onItemSelected('third')}
          style={styles.item}>
          Third
        </Text>
      </ScrollView>
    );
  }
};

var styles = StyleSheet.create({
    menuButton: {
        marginTop: 20,
        backgroundColor: '#777'
    },
    menu: {
      flex: 1,
      width: window.width,
      height: window.height,
      padding: 20,
    },
    item: {
      fontSize: 16,
      fontWeight: '300',
      paddingTop: 20,
    },    
    page: {
        flex: 1,
        alignItems: 'center',
        backgroundColor: '#777'
    },
    pageContent: {
        flex: 1,
        alignItems: 'center',
        top: 200,
    },
    menu: {
      flex: 1,
      width: window.width,
      height: window.height,
      padding: 20,
    },
    item: {
      fontSize: 16,
      fontWeight: '300',
      paddingTop: 20,
  },   
});

module.exports = MenuNavigator;

And index file should just point to Navigator:

const React = require('react-native');
const { AppRegistry, } = React;
const MenuNavigator = require('./SideMenuWithNavigation');

AppRegistry.registerComponent('MyApp', () => MenuNavigator);
like image 110
Tamir Avatar answered Oct 06 '22 05:10

Tamir


I have a starter on my Github with react-native-side-menu and navigator.

This starter also use redux (won't prevent from answering how to deal navigator + side menu).

When using sidemenu, the trick for routing is to replace previous route to prevent it from stacking (like it should in a common navigation):

  navigate(route) {
    const routeStack      = [].concat(this.refs.navigator.getCurrentRoutes());
    const previousRouteId = routeStack[routeStack.length - 1].id;
    if (route.id !== previousRouteId) {
      this.refs.navigator.replace(route);
    }

    if (this.state.sideMenuOpened) {
      this.closeSideMenu();
    }
  }

Check my starter reactNativeReduxFastStarter

fast preview of code:

import React, {
  Component
}                           from 'react';
import {
  StyleSheet,
  Text,
  Dimensions,
  Navigator,
  StatusBar
}                           from 'react-native';
import SideMenu             from 'react-native-side-menu';
import Icon                 from 'react-native-vector-icons/Ionicons';
import { AppRoutes }        from '../../../common/config';
import {
  SideMenuContent,
  Button
}                           from '../../components';
import Home                 from '../home';
import AppState             from '../appState';

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

class App extends Component {
  constructor(props) {
    super(props);
    this.init();
  }

  init() {
    this.state = {
      sideMenuOpened: false
    };
  }

  openSideMenu() {
    this.setState({
      sideMenuOpened : false
    });
  }

  closeSideMenu() {
    if (this.state.sideMenuOpened) {
      this.setState({
        sideMenuOpened : false
      });
    }
  }

  toggleSideMenu() {
    this.setState({
      sideMenuOpened: !this.state.sideMenuOpened
    });
  }

  updateSideMenuState(isOpened) {
    this.setState({
      sideMenuOpened: isOpened
    });
  }

  navigate(route) {
    const routeStack      = [].concat(this.refs.navigator.getCurrentRoutes());
    const previousRouteId = routeStack[routeStack.length - 1].id;
    if (route.id !== previousRouteId) {
      this.refs.navigator.replace(route);
    }

    if (this.state.sideMenuOpened) {
      this.closeSideMenu();
    }
  }

  renderScene(route, navigator) {
    switch (route.id) {
    case 1:
      const route1 = AppRoutes.getRouteFromRouteId(1);
      return (
        <Home
          ref={route1.refView}
          navigator={navigator}
          navigate={(toRoute)=>this.navigate(toRoute)}
        />
      );
    case 2:
      const route2 = AppRoutes.getRouteFromRouteId(2);
      return (
        <AppState
          ref={route2.refView}
          navigator={navigator}
          navigate={(toRoute)=>this.navigate(toRoute)}
        />
      );
    default:
      return (
        <Home
          ref={route1.refView}
          navigator={navigator}
          navigate={(toRoute)=>this.navigate(toRoute)}
        />
      );
    }
  }

  renderRouteMapper() {
    const routes = AppRoutes.getAllRoutes();
    return  {
      Title : (route, navigator, index, navState) => {
        const currentRouteId  = navState.routeStack[index].id;
        return (
          <Text style={styles.titleNavText}>
            {routes[currentRouteId - 1].navbar.navBarTitle}
          </Text>
        );
      },
      LeftButton : (route, navigator, index, navState) => {
        const currentRouteId  = navState.routeStack[index].id;
        return (
          <Button
            style={styles.leftNavButton}
            onPress={(e)=>this.toggleSideMenu(e)
            }>
            <Icon
              name={routes[currentRouteId - 1].navbar.navBarLeftIconName}
              size={32}
              color={'#333333'}
            />
          </Button>
        );
      },
      RightButton : (route, navigator, index, navState) => {
        return null;
      }
    };

  }

  render() {
    StatusBar.setBarStyle('light-content', true);
    const DEFAULT_ROUTE = { id: 1, refView: 'HomeView' };

    return (
      <SideMenu
        menu={<SideMenuContent
                backGndColor="#ECECEC"
                navigate={(route)=>this.navigate(route)}
              />}
        isOpen={this.state.sideMenuOpened}
        onChange={(isOpened) => this.updateSideMenuState(isOpened)}
        bounceBackOnOverdraw={false}
        openMenuOffset={SCREEN_WIDTH * 0.8}
        >
        <Navigator
          ref="navigator"
          initialRoute={ DEFAULT_ROUTE }
          sceneStyle={ styles.navigator }
          renderScene={(route, navigator)=>this.renderScene(route, navigator)}
          configureScene={()=>Navigator.SceneConfigs.FadeAndroid}
          navigationBar={
            <Navigator.NavigationBar
              routeMapper={this.renderRouteMapper()}
              style={styles.navBar}
            />
          }
        />
      </SideMenu>
    );
  }
}

const styles = StyleSheet.create({
  navigator: {
    backgroundColor: '#fff',
    borderLeftWidth: 0.5,
    borderLeftColor: '#F1F1F1',
  },
  navBar: {
    backgroundColor: '#fff',
    borderWidth:      0.5,
    borderColor:    '#F1F1F1'
  },
  leftNavButton : {
    flex            : 1,
    flexDirection   : 'column',
    alignItems      : 'center',
    marginTop       : 4,
    paddingTop      : 0,
    paddingBottom   : 10,
    paddingLeft     : 20,
    paddingRight    : 10
  },
  rightNavButton : {
    flex            : 1,
    flexDirection   : 'column',
    alignItems      : 'center',
    marginTop       : 4,
    paddingTop      : 6,
    paddingBottom   : 10,
    paddingLeft     : 10,
    paddingRight    : 10
  },
  titleNavText : {
    marginTop   : 14,
    color       : '#333333'
  }
});

export default App;
like image 21
MacKentoch Avatar answered Oct 06 '22 05:10

MacKentoch