Could not navigate to another screen when using to

2019-06-14 10:45发布

问题:

I'm currently developing an app using react native, right now my issue is that i couldn't navigate to main screen after login. Below is my code.

This is App.js (EDITED)

import React from 'react';
import { Loading } from './components/common/';
import TabNavigator from './screens/TabNavigator';
import AuthNavigator from './screens/AuthNavigator';
import MainNavigator from './screens/MainNavigator';
import deviceStorage from './services/deviceStorage.js';
import { View, StyleSheet } from 'react-native';

export default class App extends React.Component {
constructor() {
super();
this.state = {
  token: '',
  loading: true
}

this.newJWT = this.newJWT.bind(this);
this.deleteJWT = deviceStorage.deleteJWT.bind(this);
this.loadJWT = deviceStorage.loadJWT.bind(this);
this.loadJWT();

}

state = {
isLoadingComplete: false,
};

newJWT(token){
this.setState({
    token: token
});
}

render() {
if (this.state.loading) {
  return (
    <Loading size={'large'} />
   );
} else if (!this.state.token) {
  return (
        <View style={styles.container}>
            <AuthNavigator screenProps = {{setToken:this.newJWT}} />
        </View>
  );
} else if (this.state.token) {
  return (
        <View style={styles.container}>
            <MainNavigator screenProps = {{token: this.state.token, 
deleteJWT:this.deleteJWT,}} />
        </View>
  );
}
}
}    

This is Login.js (EDITED-v2)

import React, { Component, Fragment } from 'react';
import { Text, View, StyleSheet, ImageBackground, KeyboardAvoidingView, 
TouchableOpacity, TextInput, Alert } from 'react-native';
import axios from 'axios';
import deviceStorage from '../services/deviceStorage';

class Login extends Component {

constructor(props) {

    super(props)

    this.state = {
        username: '',
        password: '',
        error: '',
        loading: false
    };

    this.loginUser = this.loginUser.bind(this);
    this.onLoginFail = this.onLoginFail.bind(this);

}

loginUser() {
    const { username, password, password_confirmation } = this.state;

    this.setState({ error: '', loading: true });

    // NOTE Post to HTTPS only in production
    axios.post("http://192.168.1.201:8000/api/login",{
        username: username,
        password: password
    })
    .then((response) => {
        console.log('response',response)
    deviceStorage.saveKey("token", response.data.token);
    console.log(response.data.token);
    this.props.newJWT(response.data.token);
    })
    .catch((error) => {
    const status = error.response.status
    if (status === 401) {
        this.setState({ error: 'username or password not recognised.' });
    }
    this.onLoginFail();
    //console.log(error);
    //this.onLoginFail();
    });
}

onLoginFail() {
    this.setState({
        error: 'Login Failed',
        loading: false
    });
}

render() {
// other codes here
}

const styles = StyleSheet.create({
// other codes here
});
export { Login };

This is TabNavigator.js (Added)

import React from 'react';
import { Text } from 'react-native';
import { createMaterialTopTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Profile from '../screens/Profile';

const TabNavigator = createMaterialTopTabNavigator(
{
Profile: {
screen: props => <Profile {...props.screenProps} />,
navigationOptions: {
    tabBarIcon: ({ tintColor, focused }) => (
  <Ionicons
    name={focused ? 'ios-person' : 'ios-person'} //TODO change to focused 
icon
    size={30}
    style={{ color: tintColor }}
  />
),
}
},

},
{ initialRouteName: 'Profile',
 tabBarPosition: 'top',
 swipeEnabled: false,
 animationEnabled: true,
 lazy: true,
 tabBarOptions: {
  showLabel: false,
  showIcon: true,
  activeTintColor: 'orange',
  inactiveTintColor: 'orange',
  style: {
    backgroundColor: '#555',
  },
  indicatorStyle: {
    color: '#orange'
  }
}
}
);

const screenTitles = {
Profile: { title: 'Profiler' },
Home: { title: 'Home' },
};

TabNavigator.navigationOptions = ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
const headerTitle = screenTitles[routeName].title;
const tabBarVisible = false;
return {
headerTitle,
tabBarVisible
};
};

export default TabNavigator;

This is my AuthLoadingScreen.js

import React from 'react';
import { View } from 'react-native';
import { Login } from '../screens/Login';

class AuthLoadingScreen extends React.Component {
constructor(props){
super(props);
this.state = {
  showLogin: true
};
this.whichForm = this.whichForm.bind(this);
this.authSwitch = this.authSwitch.bind(this);
}

authSwitch() {
this.setState({
  showLogin: !this.state.showLogin
});
}

whichForm() {
if(this.state.showLogin){
  return(
    <Login newJWT={this.props.newJWT} authSwitch={this.authSwitch} />

  );
} else {

}
}

render() {
return(
  <View style={styles.container}>
    {this.whichForm()}
  </View>
);
}
}

export default AuthLoadingScreen;

const styles = {
// style codes here
};

Lastly, this is my Profile.js

import React, { Component } from 'react';
import { View, Text, TouchableOpacity, Alert, Platform } from 
'react-native';
import { Button, Loading } from '../components/common/';
import axios from 'axios';

export default class Profile extends Component {
constructor(props){
super(props);
this.state = {
  loading: true,
  email: '',
  name: '',
  error: ''
}
}

componentDidMount(){
this.onLocationPressed();
const headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer ' + this.props.token
};
axios({
  method: 'GET',
  url: 'http://192.168.1.201:8000/api/user',
  headers: headers,
}).then((response) => {
  console.log('response',response)
  console.log('response2',this.props.token)
  this.setState({
    email: response.data.user.email,
    name: response.data.user.name,
    loading: false
  });
}).catch((error) => {
  console.log(error);
  this.setState({
    error: 'Error retrieving data',
    loading: false
  });
});
}

render() {
const { container, emailText, errorText } = styles;
const { loading, email, name, error } = this.state;

if (loading){
  return(
    <View style={container}>
      <Loading size={'large'} />
    </View>
  )
} else {
    return(
      <View style={container}>
        <View>

            <Text style={emailText}>Your email: {email}</Text>
            <Text style={emailText}>Your name: {name}</Text>
        </View>

        <Button onPress={this.props.deleteJWT}>
          Log Out
        </Button>
      </View>
  );
}
}
}

const styles = {
// style codes here
};

I've fixed the previous problem that couldn't start the app. Right now i can see the login screen, but when i pressed login, there's a yellow box that indicates some problem. I've included the screenshot below.

Lastly i've added the deviceStorage.js

deviceStorage.js

import { AsyncStorage } from 'react-native';

const deviceStorage = {
async saveKey(key, valueToSave) {
try {
  await AsyncStorage.setItem(key, valueToSave);
} catch (error) {
  console.log('AsyncStorage Error: ' + error.message);
}
},

async loadJWT() {
try {
  const value = await AsyncStorage.getItem('token');
  if (value !== null) {
    this.setState({
      token: value,
      loading: false
    });
  } else {
    this.setState({
      loading: false
    });
  }

} catch (error) {
  console.log('AsyncStorage Error: ' + error.message);
}
},

async deleteJWT() {
try{
  await AsyncStorage.removeItem('token')
  .then(
    () => {
      this.setState({
        token: ''
      })
    }
  );
} catch (error) {
  console.log('AsyncStorage Error: ' + error.message);
}
}
};

export default deviceStorage;

Before navigate

After navigate

回答1:

This is my setup. It works like a charm. Sorry if it's a bit messy. I removed some stuff for clarity and I may have missed something:

App.js

import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { Asset, Font, Icon } from 'expo';
import { ENDPOINT, USER_TYPE } from './src/config'
import { Loading } from './src/components/common/';
import deviceStorage from './src/services/deviceStorage.js';
import TabNavigator from './src/TabNavigator';
import AuthNavigator from './src/AuthNavigator';
import MainNavigator from './src/MainNavigator';
import globalStyles from './src/globalStyles';
import './ReactotronConfig';

export default class App extends React.Component {

  constructor() {
    super();
    this.state = {
      jwt: '',
      loading: true,
    };
    this.newJWT = this.newJWT.bind(this);
    this.deleteJWT = deviceStorage.deleteJWT.bind(this);
    this.loadJWT = deviceStorage.loadJWT.bind(this);
    this.loadJWT();
  }

  state = {
    isLoadingComplete: false,
  };

  newJWT(jwt) {
    this.setState({
      jwt: jwt
    });
  }

  render() {
    if (this.state.loading) {
      return (
        <Loading size={'large'} />
       );
    } else if (!this.state.jwt) {
      //console.log(this.props, '<=== app.js');
      return (
        <View style={styles.container}>
          {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
          <AuthNavigator screenProps={{setToken: this.newJWT }} />
        </View>
      );
    } else {
      return (
        <View style={styles.container}>
          {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
          <MainNavigator
          screenProps={{ jwt: this.state.jwt,
                         deleteToken: this.deleteJWT,
                      }}
          />
        </View>
      );
    }
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
  },
});

AuthNavigator.js

import React from 'react';
import { createAppContainer, createBottomTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import AuthScreen from './screens/AuthScreen';

const AuthNavigator = createBottomTabNavigator(
  {
  Auth: (props) => {
    return <AuthScreen {...props.screenProps} />;
  }
  },
  { initialRouteName: 'Auth',
    tabBarOptions: {
      showLabel: false,
      activeBackgroundColor: '#eee',
    }
  }
);

export default createAppContainer(AuthNavigator);

MainNavigator.js

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

import TabNavigator from './TabNavigator';

  const MainNavigator = createStackNavigator({
    Main: TabNavigator },
    {
      initialRouteName: 'Main',
      defaultNavigationOptions: {
        headerTitleStyle: {
          fontSize: 20,
          textTransform: 'uppercase'
        }
      }
  });

export default createAppContainer(MainNavigator);

TabNavigator.js

import React from 'react';
import { Text } from 'react-native';
import { createMaterialTopTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import IconBadge from 'react-native-icon-badge';
import ProfileScreen from './screens/ProfileScreen';
import NotificationsScreen from './screens/NotificationsScreen';
import HomeStackNavigator from './HomeStackNavigator';
import CartStackNavigator from './CartStackNavigator';
import QuotesStackNavigator from './QuotesStackNavigator';
import InitialRoute from './InitialRoute';

const TabNavigator = createMaterialTopTabNavigator(
  {
  Profile: {
    screen: props => <ProfileScreen {...props.screenProps} />,
    navigationOptions: {
        tabBarIcon: ({ tintColor, focused }) => (
      <Ionicons
        name={focused ? 'ios-person' : 'ios-person'} //TODO change to focused icon
        size={30}
        style={{ color: tintColor }}
      />
    ),
  }
  },
  Home: HomeStackNavigator,
  Quotes: QuotesStackNavigator,
  Notifications: { screen: props => <NotificationsScreen {...props.screenProps} />,
              navigationOptions: ({ screenProps }) => ({
                tabBarIcon: ({ tintColor, focused }) => (
                  <IconBadge
                    MainElement={
                      <Ionicons
                        name={focused ? 'ios-notifications' : 'ios-notifications'}
                        size={30}
                        style={{ color: tintColor }}
                      />
                    }
                    BadgeElement={
                      <Text style={{ color: '#FFFFFF' }}>{screenProps.unreadMessagesCount}</Text>
                    }
                    IconBadgeStyle={{ width: 15,
                      height: 15,
                      position: 'absolute',
                      top: 1,
                      left: -6,
                      marginLeft: 15,
                      backgroundColor: 'red' }}
                    Hidden={screenProps.unreadMessagesCount === 0}
                  />

            )
          })

  },
  Cart: CartStackNavigator,
},
  { initialRouteName: 'Profile',
    tabBarPosition: 'top',
    swipeEnabled: false,
    animationEnabled: true,
    lazy: true,
    tabBarOptions: {
      showLabel: false,
      showIcon: true,
      activeTintColor: 'orange',
      inactiveTintColor: 'orange',
      style: {
        backgroundColor: '#555',
      },
      indicatorStyle: {
        color: '#orange'
      }
    }
  }
);

const screenTitles = {
   Profile: { title: 'Hola Maestro' },
   Home: { title: 'Selecciona la Categoría' },
   Quotes: { title: 'Mi Historial de Cotizaciones' },
   Notifications: { title: 'Notificaciones' },
   Cart: { title: 'Mi Pedido' },
};

TabNavigator.navigationOptions = ({ navigation }) => {
  const { routeName } = navigation.state.routes[navigation.state.index];
  const headerTitle = screenTitles[routeName].title;
  const tabBarVisible = false;
  return {
    headerTitle,
    tabBarVisible
  };
};

export default TabNavigator;

Login.js

import React, { Component, Fragment } from 'react';
import { Text, View, StyleSheet, ImageBackground, KeyboardAvoidingView } from 'react-native';
import axios from 'axios';
import Ionicons from 'react-native-vector-icons/Ionicons';
//import Pusher from 'pusher-js/react-native';
import { ENDPOINT, USER_TYPE } from '../config'
import deviceStorage from '../services/deviceStorage';
import { Input, TextLink, Loading, Button } from './common';
import Header from '../components/Header';

class Login extends Component {
  constructor(props){
    super(props);
    this.state = {
      username: '',
      password: '',
      error: '',
      loading: false
    };
    this.pusher = null; // variable for storing the Pusher reference
    this.my_channel = null; // variable for storing the channel assigned to this user
    this.loginUser = this.loginUser.bind(this);
  }

  loginUser() {
    const { username, password, password_confirmation } = this.state;

    axios.post(`${ENDPOINT}/login`, {
      user: {
        login: username,
        password: password
      }
    })
    .then((response) => {
      deviceStorage.saveKey("id_token", response.headers.authorization);
      this.props.newJWT(response.headers.authorization);
      //this.setPusherData();
    })
    .catch((error) => {
      this.onLoginFail();
    });
  }

  onLoginFail() {
    this.setState({
      error: 'Usuario o contraseña incorrecta',
      loading: false
    });
  }
  }

  render() {
    const { username, password, error, loading } = this.state;
    const { container, form, section, errorTextStyle, iconContainer, inputContainer, titleText } = styles;

    return (
      <View style={container}>
        <Header title="¡Bienvenido Amigo Maestro!" />
        <View style={form}>
          <ImageBackground source={require('./cemento-login.jpg')} style={{ flex: 1, marginBottom: 30 }}>
          <View style={{marginTop: 120}}>
            <Text style={titleText}>INICIAR SESIÓN</Text>
            <View style={section}>
              <View style={iconContainer}>
                <Ionicons
                  name={'ios-person'}
                  size={26}
                  style={{ color: '#fff', alignSelf: 'center' }}
                />
              </View>
              <View style={inputContainer}>
                <Input
                  placeholder="Usuario"
                  value={username}
                  onChangeText={username => this.setState({ username })}
                />
              </View>
            </View>

          <View style={section}>
            <View style={iconContainer}>
              <Ionicons
                name={'ios-lock'}
                size={26}
                style={{ color: '#fff', alignSelf: 'center' }}
              />
            </View>
            <View style={inputContainer}>
              <Input
                secureTextEntry
                placeholder="Contraseña"
                value={password}
                onChangeText={password => this.setState({ password })}
              />
            </View>
          </View>
          </View>
          </ImageBackground>

        </View>
        <KeyboardAvoidingView
          behavior="padding"
          keyboardVerticalOffset={30}
        >
          <TextLink style={{ }} onPress={this.props.formSwitch}>
            Aún no estas registrado? Regístrate
          </TextLink>
          <TextLink style={{ }} onPress={this.props.forgotPassword}>
            Olvidaste tu contraseña?
          </TextLink>
          <Text style={errorTextStyle}>
            {error}
          </Text>

          {!loading ?
            <Button onPress={this.loginUser}>
              Ingresar
            </Button>
            :
            <Loading size={'large'} />}
        </KeyboardAvoidingView>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  form: {
    flex: 0.8
  },
  section: {
    flexDirection: 'row',
    backgroundColor: '#eee',
    borderRadius: 3,
    marginTop: 10,
    height: 40,
    marginLeft: '10%',
    marginRight: '10%',
  },
  titleText: {
    color: '#fff',
    alignSelf: 'center',
    fontSize: 20,
    marginBottom: 10
  },
  errorTextStyle: {
    alignSelf: 'center',
    fontSize: 18,
    color: 'red'
  },
  iconContainer: {
    flex: 0.1,
    height: 40,
    borderRadius: 3,
    alignSelf: 'center',
    justifyContent: 'center',
    backgroundColor: 'orange',
  },
  inputContainer: {
    flex: 0.8,
    alignSelf: 'flex-start',
    marginLeft: -70,
  }
});

export { Login };


回答2:

Inside App.js you never change loading to false so the render method never gets to any of the other conditions. Once you have received the token you need to update state and change loading.