【问题标题】:Full working example of react-native-side-menu with routes or navigation [closed]带有路线或导航的 react-native-side-menu 的完整工作示例 [关闭]
【发布时间】:2016-08-14 17:57:36
【问题描述】:

我发现很少有帖子给出了一些关于如何将导航\路由与 react-native-side-menu 集成的“提示”,遗憾的是没有找到任何显示此类功能的完整工作示例的帖子。

我也不确定导航\路线最简单的实现是什么,以及这两个选项之间有什么区别(当然这不是专门针对侧边菜单,但在我的情况下应该结合在一起)。

任何人都可以指出这样的例子吗?

【问题讨论】:

    标签: menu routes navigation react-native


    【解决方案1】:

    所以最终我能够集成标准的 Navigator,如 Facebook 的官方示例代码所示:

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

    使用 react-native-side-menu 组件,整个代码如下所示:

    '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;
    

    并且索引文件应该只指向导航器:

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

    【讨论】:

    • 有点棘手但有效!谢谢!
    • 我想在单击侧面菜单中的注销图标时显示警报。这段代码有可能吗?
    • 代替 this.props.onItemSelected 您可以为菜单中某些特定项目的 onPress 事件调用任何其他方法
    【解决方案2】:

    如果您指的是抽屉菜单,请检查 react-native-material-design 和给定的演示应用。

    【讨论】:

    【解决方案3】:

    我的 Github 上有一个带有 react-native-side-menunavigator 的启动器。

    这个初学者也使用redux(不会阻止回答如何处理导航器+侧边菜单)。

    在使用侧边菜单时,routingtrick 用于替换之前的路线以防止其堆叠(就像在普通导航中一样):

      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();
        }
      }
    

    检查我的启动器reactNativeReduxFastStarter

    快速预览代码:

    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;
    

    【讨论】:

      【解决方案4】:

      你可以在 github 上查看这个完整的 sidemenu 项目。本项目包含ToolbarAndroid、路由、DrawerLayoutAndroid、溢出菜单等组件。

      https://github.com/darde/react-native-sidemenu

      【讨论】:

        猜你喜欢
        • 2016-09-22
        • 1970-01-01
        • 2011-08-19
        • 2022-10-16
        • 2018-04-24
        • 2016-08-14
        • 1970-01-01
        • 2011-10-16
        • 1970-01-01
        相关资源
        最近更新 更多