【问题标题】:How to use react navigation outside of a screen如何在屏幕外使用反应导航
【发布时间】:2019-04-22 01:12:31
【问题描述】:

我想让我的应用程序在正确输入代码后移至下一页,但我在这样做时遇到了很多麻烦。我正在使用文件名 AccessForm.js ,它不是屏幕,而是包含在访问代码屏幕中的组件。我尝试使用this.props.navigation.navigate('CreateAccountScreen');,但遇到错误“未定义不是对象(评估'this.props.navigation')。经过反复试验,我发现我只能在实际中使用react-navigation出于某种奇怪的原因屏幕。在此之后,我尝试使用 this.statethis.setState({}) 来跟踪屏幕变量,并将其同步到实际的访问代码屏幕,所以我可以使用导航。不幸的是,@987654324 @ 还会引发“未定义不是对象”错误。我在下面粘贴了我的代码的缩写版本。在屏幕文件问题之外实现此导航的最佳方法是什么?

App.js ---->

import { createStackNavigator, createAppContainer } from 'react-navigation';

import AccessScreen from './src/screens/AccessScreen';
import CreateAccountScreen from './src/screens/CreateAccountScreen';

const RootStack = createStackNavigator ({
    EnterAccessCode : {
        screen: AccessScreen
    },
    CreateAccount : {
        screen: CreateAccountScreen
    }
},
{
  headerMode: 'none'
});

const App = createAppContainer(RootStack);

export default App;

AccessForm.js ---->

import React from 'react';
import { StyleSheet, Text, View, TextInput, AlertIOS } from 'react-native';


var firebase = require("firebase");

if (!firebase.apps.length) { // Don't open more than one firebase session
  firebase.initializeApp({ // Initialize firebase connection
    apiKey: "key",
    authDomain: "domain",
    databaseURL: "url",
    storageBucket: "storage_bucket",
  });
}

this.codesRef = firebase.database().ref('codes'); // A reference to the codes section in the db

// this.state = {
//   screen: 0
// };

export default class LoginForm extends React.Component {
  constructor(props) {
    super(props);
    //this.checkCode = this.checkCode.bind(this); // throws error
  }
  render() {
    return (
      <View style={styles.container} >
        <TextInput
            style={styles.input}
            placeholder='Access Code'
            returnKeyType='go'
            onSubmitEditing={(text) => checkCode(text.nativeEvent.text)} // Checks the code entered
            autoCapitalize='none'
            autoCorrect={false}
        />
      </View>
    );
  }
}


function checkCode(text) {
  var code = text; // Set entered code to the var "code"
  var identifier = ""; // Used to store unique code object identifier
  codesRef.once('value', function(db_snapshot) {
    let codeIsFound = false
    db_snapshot.forEach(function(code_snapshot) { // Cycle through available codes in db
      if (code == code_snapshot.val().value) { // Compare code to db code
        codeIsFound = true;
        identifier = code_snapshot.key; // Code object ID
      }
    })
    if (codeIsFound) {
      deleteCode(identifier); // Delete the code if used, maybe do this after account is created?
      this.props.navigation.navigate('CreateAccountScreen');
      //this.setState({screen: 1}); // this throws error
      // MOVE TO NEXT SCREEN
      //this.props.navigation.navigate('AccountCreateScreen'); // throws error
    } else { // wrong code
      // note to self : add error message based on state var
      AlertIOS.alert("We're Sorry...", "The code you entered was not found in the database! Please contact Mr. Gibson for further assistance.");
    }
  });
}

function deleteCode(id) { // delete a code from unique ID
  firebase.database().ref('codes/' + id).remove();
}

// stylesheet is below

Login.js ---->

import React from 'react';
import { StyleSheet, Text, View, Image, TextInput, KeyboardAvoidingView, Platform } from 'react-native';
import AccessForm from './AccessForm';

export default class App extends React.Component {
  render() {
    return (
        <View>
            <View style={styles.logoContainer}>
                <Image 
                    source={require('../images/mhs.jpg')}
                    style={styles.logo}
                />
                <Text style={styles.app_title}>MHS-Protect</Text>
                <Text>An app to keep MHS safe and in-touch.</Text>
            </View>
            <KeyboardAvoidingView style={styles.container} behavior='padding'>
              <View style ={styles.formContainer}>
                  <AccessForm/>
              </View>
            </KeyboardAvoidingView>
        </View>
    );
  }
}

//styles below

【问题讨论】:

  • 1. import { withNavigation } from 'react-navigation'; 2. 从您的班级中删除 export default。 3.在底部做export default withNavigation(LoginForm)
  • @Ziyo 我想我已经很接近了,但这仍然会抛出“未定义不是对象(评估 this.props.navigation)”
  • 哦,不。 checkCode函数应该是LoginForm的方法
  • 如果你把checkCode放在类里面,你就不需要做withNavigation的事情了。
  • 我会把它作为答案发布在下面。

标签: javascript react-native react-navigation react-native-ios


【解决方案1】:

import React from 'react';
import { StyleSheet, Text, View, TextInput, AlertIOS } from 'react-native';

var firebase = require('firebase');

if (!firebase.apps.length) {
  // Don't open more than one firebase session
  firebase.initializeApp({
    // Initialize firebase connection
    apiKey: 'key',
    authDomain: 'domain',
    databaseURL: 'url',
    storageBucket: 'storage_bucket',
  });
}

export default class LoginForm extends React.Component {
  constructor(props) {
    super(props);
    this.codesRef = firebase.database().ref('codes'); // A reference to the codes section in the db
  }

  checkCode = text => {
    var code = text; // Set entered code to the var "code"
    var identifier = ''; // Used to store unique code object identifier
    this.codesRef.once('value', function(db_snapshot) {
      let codeIsFound = false;
      db_snapshot.forEach(function(code_snapshot) {
        // Cycle through available codes in db
        if (code == code_snapshot.val().value) {
          // Compare code to db code
          codeIsFound = true;
          identifier = code_snapshot.key; // Code object ID
        }
      });
      if (codeIsFound) {
        this.deleteCode(identifier); // Delete the code if used, maybe do this after account is created?
        this.props.navigation.navigate('CreateAccount');
      } else {
        // wrong code
        // note to self : add error message based on state var
        AlertIOS.alert(
          "We're Sorry...",
          'The code you entered was not found in the database! Please contact Mr. Gibson for further assistance.'
        );
      }
    });
  };

  deleteCode = id => {
    firebase
      .database()
      .ref('codes/' + id)
      .remove();
  };

  render() {
    return (
      <View style={styles.container}>
        <TextInput
          style={styles.input}
          placeholder="Access Code"
          returnKeyType="go"
          onSubmitEditing={text => this.checkCode(text.nativeEvent.text)} // Checks the code entered
          autoCapitalize="none"
          autoCorrect={false}
        />
      </View>
    );
  }
}

【讨论】:

  • 仍然在唠叨“未定义不是一个对象(评估'this.props.navigation')”......你认为其他地方可能有问题吗?我看不出这段代码不能按预期工作的任何原因。
  • 我已将所有相关文件的代码添加到问题中,如果有帮助,我可以制作一个保管箱链接或其他东西。十分感谢你的帮助!我对此很陌生
  • 你有CreateAccount 而不是CreateAccountScreen。尝试导航到CreateAccount
  • 我改变了它,但仍然有同样的错误 :( 。我还将 onSubmitEditing={text =&gt; this.checkCode(text.nativeEvent.text)} 更改为 onSubmitEditing={() =&gt; this.props.navigation.navigate('CreateAccount')} 以查看是否可以直接导航到那里,但它无济于事。
【解决方案2】:

你的道具中应该有navigation 对象。默认情况下,反应导航会将navigation 传递给除其他组件之外的所有屏幕。为此,您有两种选择:
1. 将屏幕上的navigation 道具传递给每个子组件(不推荐)。
2.使用withNavigation作为文档中提到的https://reactnavigation.org/docs/en/connecting-navigation-prop.html

import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';

class MyBackButton extends React.Component {
  render() {
    return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
  }
}

// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);

编辑: checkCode 方法不属于您的LoginForm。您需要:
1. 使其成为 LoginForm 的一部分。
2.记得使用bindarrow function定义。否则,您的 this 内部函数未定义。

import { withNavigation } from 'react-navigation';
class LoginForm extends React.Component {
    checkCode = (text) => {
        ....
    };
}
export default withNavigation(LoginForm);

你可以在这里阅读更多关于bind或箭头方法https://medium.com/shoutem/react-to-bind-or-not-to-bind-7bf58327e22a

【讨论】:

  • 这仍然会抛出“undefined is not an object (evalating this.props.navigation)”
  • 其实把我的onSubmitEditing改成() => this.props.navigation.navigate('CreateAccountScreen'),没有报错,但是没有效果。我的最终目标是在 checkCode 函数中成功实现屏幕更改。
  • checkCode 方法不属于您的LoginForm。您需要: 1. 使其成为 LoginForm 的一部分。 2.记得使用bindarrow function定义。否则,您的 this 内部函数未定义。 ``` import { withNavigation } from 'react-navigation';类 LoginForm 扩展 React.Component { checkCode = (text) => { .... }; } 导出默认 withNavigation(LoginForm); ```你可以在这里阅读更多关于bind或箭头方法medium.com/shoutem/react-to-bind-or-not-to-bind-7bf58327e22a
  • @Huy_Ngo 非常感谢您的提示,但我已经尝试了两个代码段并且仍然收到完全相同的错误“未定义不是对象(评估'this.props.navigation')”。是否有可能在不同的地方有其他问题?我想不出您/ Zigos 的答案无法正常工作的任何原因。
【解决方案3】:

复制并粘贴(参考)来自:https://github.com/react-navigation/react-navigation/issues/1439#issuecomment-303661539

对我有用。

您可以将顶级导航器引用传递给服务,并从该服务分派操作。

// App.js

import NavigatorService from './services/navigator';

const Navigator = StackNavigator({ /* ... */ })

class App extends Component {
  // ...

  render(): {
    return (
      <Navigator
        ref={navigatorRef => {
          NavigatorService.setContainer(navigatorRef);
        }}
      />
    );
  }
}
// services/navigator.js
// @flow

import { NavigationActions } from 'react-navigation';
import type { NavigationParams, NavigationRoute } from 'react-navigation';

let _container; // eslint-disable-line

function setContainer(container: Object) {
  _container = container;
}

function reset(routeName: string, params?: NavigationParams) {
  _container.dispatch(
    NavigationActions.reset({
      index: 0,
      actions: [
        NavigationActions.navigate({
          type: 'Navigation/NAVIGATE',
          routeName,
          params,
        }),
      ],
    }),
  );
}

function navigate(routeName: string, params?: NavigationParams) {
  _container.dispatch(
    NavigationActions.navigate({
      type: 'Navigation/NAVIGATE',
      routeName,
      params,
    }),
  );
}

function navigateDeep(actions: { routeName: string, params?: NavigationParams }[]) {
  _container.dispatch(
    actions.reduceRight(
      (prevAction, action): any =>
        NavigationActions.navigate({
          type: 'Navigation/NAVIGATE',
          routeName: action.routeName,
          params: action.params,
          action: prevAction,
        }),
      undefined,
    ),
  );
}

function getCurrentRoute(): NavigationRoute | null {
  if (!_container || !_container.state.nav) {
    return null;
  }

  return _container.state.nav.routes[_container.state.nav.index] || null;
}

export default {
  setContainer,
  navigateDeep,
  navigate,
  reset,
  getCurrentRoute,
};

然后你就可以在任何地方使用 Navigator 服务了。

喜欢:

import NavigatorService from './services/navigator';

NavigatorService.navigate('Home');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    • 2018-12-30
    • 1970-01-01
    相关资源
    最近更新 更多