React Native Navigator路由

时间:2016-06-15 08:37:19

标签: javascript android react-native navigation-drawer react-router

我想在我的react-native App中插入一个定义路线的导航。我找到了Navigate.js。不幸的是,它在我的应用中无法正常运行。指示以下错误:

Null不是对象(评估' route.component')

我在代码中找不到任何错误。我不明白为什么没有定义对象。但是,在demo它完美地运作。你能帮我么?

image

index.android.js:

import React, { Component } from 'react';
import {
  AppRegistry, Navigator, DrawerLayoutAndroid, ScrollView, View, Text
} from 'react-native';

import Navigate from './src/utils/Navigate';
import Navigation from './src/scenes/Navigation';
import Home from './src/views/Home';

class app extends Component {
  static childContextTypes = {
    drawer: React.PropTypes.object,
    navigator: React.PropTypes.object
  };

  constructor(props) {
    super(props);
    this.state = {
      drawer: null,
      navigator: null
    };
  }

  getChildContext = () => {
    return {
      drawer: this.state.drawer,
      navigator: this.state.navigator
    }
  };

  setDrawer = (drawer) => {
    this.setState({
      drawer
    });
  };

  setNavigator = (navigator) => {
    this.setState({
      navigator: new Navigate(navigator)
    });
  };

  render() {
    const { drawer, navigator } = this.state;
    const navView = React.createElement(Navigation);

    return (
      <DrawerLayoutAndroid
        drawerWidth={300}
        drawerPosition={DrawerLayoutAndroid.positions.Left}
        renderNavigationView={() => {
                    if (drawer && navigator) {
                        return navView;
                    }
                    return null;
                }}
        ref={(drawer) => { !this.state.drawer ? this.setDrawer(drawer) : null }}
      >
        {drawer &&
        <Navigator
          initialRoute={Navigate.getInitialRoute()}
          navigationBar={<Toolbar onIconPress={drawer.openDrawer} />}
          configureScene={() => {
                            return Navigator.SceneConfigs.FadeAndroid;
                        }}
          ref={(navigator) => { !this.state.navigator ? this.setNavigator(navigator) : null }}
          renderScene={(route) => {
                        if (this.state.navigator && route.component) {
                            return (
                                <View
                                    style={styles.scene}
                                    showsVerticalScrollIndicator={false}>
                                  <route.component title={route.title} path={route.path} {...route.props} />
                                </View>
                            );
                        }
                    }}
        />
        }
      </DrawerLayoutAndroid>
    );
  }
}

AppRegistry.registerComponent('app', () => app);

const styles = {
  scene: {
    flex: 1,
    marginTop: 56
  }
};

routes.js:

export default {
    home: {
        initialRoute: true,
        title: 'Home',
        component: require('./views/Home').default,
    },

    hotels: {
        title: 'Hotels',
        component: require('./views/Hotels').default
    },

}

./ src / utils / Navigate.js:

import { BackAndroid } from 'react-native';

let routes = null;

try {
  routes = require('../routes').default;
} catch (e) {
}

export default class Navigate {

  /**
  * Gets the initial root props
  * Accepts a parent route name and finds that routes props
  *    OR - Finds the first parent route with 'initialRoute' set to true.
  *    OR FINALLY - Returns the first parent route.
  * @param path
  * @param customRoutes
  * @returns {{path: *}}
  */
  static getInitialRoute = (path, customRoutes) => {
    if (customRoutes) {
      routes = customRoutes;
    }
    if (!routes) {
      console.warn(`[Navigate.getInitialRoute()] No routes found. Add routes to src/routes.js.`);
      return null;
    }
    if (path) {
      return {
        path,
        ...routes[path]
      }
    } else {
      let initial;
      for (const route in routes) {
        if (routes[route].initialRoute) {
          initial = {path: route, ...routes[route]};
          break;
        }
      }
      return initial || {
        path,
        ...routes[Object.keys(routes)[0]]
      }
    }
  };

  constructor(navigator) {
    this.navigator = navigator;
    this.savedInstanceStates = new Map();
    this.currentRoute = null;
    this.previousRoute = null;
    this.isChild = false;
    BackAndroid.addEventListener('hardwareBackPress', this._hardwareBackPress);
  }

  /**
  * Generates a pretty name based off the last item in a route path.
  * Mainly for titles
  * @param path
  * @returns {string}
  * @private
  */
  _getPathPrettyName = (path) => {
    path = path.split('.');
    if (path.length === 1) {
      path = path[0]
    } else {
      path = path[path.length - 1];
    }
    return path.charAt(0).toUpperCase() + path.slice(1);
  };

  /**
  * Handle hardware back press
  * @returns {boolean}
  */
  _hardwareBackPress = () => {
    if (this.navigator.getCurrentRoutes()[0].path == Navigate.getInitialRoute().path) {
      BackAndroid.exitApp();
      return false;
    } else {
      if (!this.isChild) {
        route = Navigate.getInitialRoute();
        this.currentRoute = route;
        this.navigator.replace(route);
        return true;
      } else {
        this.back();
        return true;
      }
    }
  };

  /**
  * Deep get an object without passing in the 'children' key
  * @param path
  * @returns object
  * @private
  */
  _getRouteObject = (path) => {
    let obj = routes;
    const properties = path.replace(/\./g, '.children.').split('.');
    if (properties.length === 1) return obj[path];
    properties.forEach(function (key) {
      if (!obj || !hasOwnProperty.call(obj, key)) {
        obj = undefined;
        return;
      }
      obj = obj[key];
    });
    return obj;
  };

  _saveInstanceState = (path, instanceState) => {
    if (instanceState) {
      this.savedInstanceStates.set(path, instanceState);
    }
  };

  _recoverInstanceState = (path) => {
    const instanceState = this.savedInstanceStates.get(path);
    if (instanceState) {
      this.savedInstanceStates.delete(path);
    }
    return instanceState || null;
  };
  /**
  * Jump to a component at a certain path defined in routes
  * @param path
  * @param title
  * @param props
  */
  to = (path, title, props) => {
    if (!path) {
      console.warn(`[Navigate.to(undefined)] A route path is required to navigate to`);
    } else {
      const obj = this._getRouteObject(path);

      if (!obj || !obj.component) {
        console.warn(`[Navigate.to(${path})] No component exists at this path`);
      } else {
        this.isChild = path.split('.').length > 1;
        const route = {
          title: title ? title : (obj.title ? obj.title : path),
          path,
          component: obj.component,
          props
        };
        this.previousRoute = this.currentRoute;
        this.currentRoute = route;
        this.navigator.replace(route);
      }
    }
  };

  /**
  * Go back to the parent of the current component
  * @param title
  * @param props
  */
  back = (title, props) => {
    const current = this.navigator.getCurrentRoutes()[0].path;
    const path = current.substr(0, current.lastIndexOf('.'));
    const obj = this._getRouteObject(path);
    const savedInstance = this._recoverInstanceState(path); 

    if (!obj) {
      console.warn(`[Navigate.back()] No component exists for the parent of ${current}`);
    } else {
      this.isChild = path.split('.').length > 1;
      const route = {
        // title: title ? title : (obj.title || this._getPathPrettyName(path)),
        title: title ? title : (this.previousRoute ? this.previousRoute.title : (obj.title || this._getPathPrettyName(path))),
        path,
        component: obj.component,
        props
      };

      this.currentRoute = route;
      this.navigator.replace(route);
    }
  };

  /**
  * Go forward to a defined child component of the current route or the first child that exists
  * @param {String} child [Optional] Specify the name of the child to go to.
  * @param {String} title [Optional] Override the routes default title.
  * @param {Object} props [Optional] Send additional props that'll get bootstrapped onto the route
  * @param {Object} savedInstanceState [Optional] Send additional props that'll get bootstrapped onto the route
  */
  forward = (child, title, props, savedInstanceState) => {
    const current = this.navigator.getCurrentRoutes()[0].path;
    const currentObject = this._getRouteObject(current);

    if (!currentObject.children || !Object.keys(currentObject.children).length) {
      console.warn(`[Navigate.forward()] No child components exists for ${current}`);
    } else {
      this.isChild = true;
      if (child) {
        const obj = this._getRouteObject(`${current}.${child}`);
        if (!obj) {
          console.warn(`[Navigate.forward(${child})] Child component ${child} does not exist on ${current}`);
        } else {
          const route = {
            title: title ? title : (obj.title || this._getPathPrettyName(`${current}.${child}`)),
            path: `${current}.${child}`,
            component: obj.component,
            props
          };
          this.previousRoute = this.currentRoute;
          this.currentRoute = route;
          this.navigator.replace(route);
        }
      } else {
        const path = `${current}.${Object.keys(currentObject.children)[0]}`;
        const obj = this._getRouteObject(path);
        const route = {
          title: title ? title : (obj.title ? obj.title : this._getPathPrettyName(path)),
          path,
          component: obj.component,
          props
        };
        this.currentRoute = route;
        this.navigator.replace(route);
      }
    }
  };

  /**
  * Returns the current route config.
  * @returns {*|makeAction}
  */
  getRoutes = () => {
    return routes;
  };

  setRoutes = (newRoutes) => {
    routes = newRoutes;
  };

};

/src/views/Home.js:

import React from "react";
import {View, StyleSheet, ScrollView, Text} from "react-native";

export class Home extends React.Component {

  static contextTypes = {
    navigator: React.PropTypes.object.isRequired
  };

    render() {
      const { navigator } = this.context;
      const theme = AppStore.getState().theme;

    return <View style={styles.container}>
      <Text>...</Text>
    </View>;
  }
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

1 个答案:

答案 0 :(得分:0)

问题可能在导航文件中。您的方法getInitialRoute需要customRoutes不能为null / undefined,否则它将返回null。

我假设您不知道如何调试。您可以阅读如何调试here