I am developing an application and using the listener onAuthStateChanged from Firebase in my AuthenticationLoadingScreen so I can redirect the user based on this state, however, it just opens a white page and doesn't open the home screen.
EDIT: Someone says that maybe the app.js is the problem so I added it here
import HomeScreen from '../screens/HomeScreen'
import AuthScreen from '../screens/AuthScreen'
// Implementation of HomeScreen, OtherScreen, SignInScreen, AuthLoadingScreen
// goes here.
const AppStack = createStackNavigator({ Home: HomeScreen});
const AuthStack = createStackNavigator({ Home: HomeScreen });
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: AuthScreen,
App: AppStack,
Auth: AuthStack
},
{
initialRouteName: 'AuthLoading',
}
));
export default class AuthScreen extends React.Component {
constructor(props){
super(props);
}
componentDidMount = ()=>{
firebase.auth().onAuthStateChanged(user => {
this.props.navigation.navigate(user ? 'App' : 'Auth');
});
}
render() {
return (
<ImageBackground
source={require('../assets/images/AuthScreenBackground.png')}
style={styles.ImageBackground}/>
);
}
}
Here is the App.js code:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import AppNavigation from './navigation/AppNavigation.js'
import * as firebase from 'firebase'
var config = {
};
firebase.initializeApp(config);
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<AppNavigation/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
Try this code:
componentWillMount() {
this.observeLogin = firebase.auth().onAuthStateChanged(user => {
if (user) {
this.props.navigation.navigate('App');
} else {
this.props.navigation.navigate('Auth');
}
});
}
componentWillUnmount() {
// Don't forget to unsubscribe when the component unmounts
this.observeLogin ();
}
Related
I am trying different ways to display a conditional button based on the athtentication state, but i keep getting into trouble. I have an app.js that defines the stacknavigator, which adds a button to the header giving the option to log out if authenticated.
I wrote a handleLogout function that should perform this.
import React from 'react';
import { Button, Image, View, Text } from 'react-native';
import firebase from 'react-native-firebase';
import Loading from './Loading';
import SignUp from './SignUp';
import Login from './Login';
import Main from './Main';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import { useNavigation } from '#react-navigation/native';
// eslint-disable-next-line no-undef
handleLogOut = () => {
const navigation = useNavigation();
firebase
.auth()
.signOut()
.then(() => this.props.navigation.navigate('Login'))
.catch(error => this.setState({errorMessage: error.message}));
};
const AppNavigator = createStackNavigator(
{
Loading: Loading,
SignUp: SignUp,
Login: Login,
Main: Main,
},
{
initialRouteName: 'Loading',
defaultNavigationOptions: {
headerLeft: null,
headerRight: () => {
let button = this.loggedIn? (
<Button
onPress={this.handleLogOut}
title="Log-out"
color="#fff"
/>
)
:
(
<Button
onPress={() => alert('Please log in')}
title="Log-in"
color="#fff"
/>
)
return button;
},
headerStyle: {
backgroundColor: '#c6f1e7',
},
headerTintColor: '#59616e',
headerTitleStyle: {
fontFamily: 'Raleway-Regular',
fontWeight: '400',
},
},
},
);
export default createAppContainer(AppNavigator);
App.js calls on loading.js where the value for loggedin is declared, based on the authentciation state and then loads either main.js or sign-up. in this case the main page is loaded, which means that someone is authenticated:
// Loading.js
import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';
import firebase from 'react-native-firebase';
export default class Loading extends React.Component {
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({ loggedIn: true })
this.props.navigation.navigate(user ? 'Main' : 'SignUp');
} else {
this.setState({ loggedIn: false })
}
});
}
render() {
return (
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#c6f1e7',
},
});
Now the page redirects to main and shows the welcome message, which indicates that the user is logged in, but the button in the header is saying 'log-in' as well, which means the button is not chosen well. I assume that this is because the loggedin value is not read and it automatically sets it on loggedin: false.
Here is the code for main.js
// Main.js
import React from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import firebase from 'react-native-firebase';
import { createAppContainer } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import Kowops from './Kowops';
import Scan from './Scan';
import Wallet from './Wallet';
export class Main extends React.Component {
state = { currentUser: null }
componentDidMount() {
const { currentUser } = firebase.auth()
this.setState({ currentUser })
}
render() {
const { currentUser } = this.state
return (
<View style={styles.container}>
<Text>
Hidiho {currentUser && currentUser.email}!
</Text>
</View>
)
}
}
const bottomTabNavigator = createBottomTabNavigator(
{
Main: {screen: Main},
Kowops: {screen:Kowops},
Scan: {screen:Scan},
Wallet: {screen:Wallet},
},
{
//initialRouteName: 'Main',
tabBarOptions: {
initialRouteName: 'Main',
activeTintColor: '#59616e',
inactiveTintColor: '#a9a9a9',
style: {
backgroundColor: '#c6f1e7',
}
},
});
export default createAppContainer(bottomTabNavigator);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})
So I need to figure out how to ensure that the value of isloggedin is read properly and the script loads the right button.
Does anyone have a clue?
Thanks in advance!!
Tim
The key here is that you can't use state across different components without passing them as props or through navigation params in this case. You can't use the useNavigation hook outside of a functional component so you should pass the navigation object around when you need it outside of a component (handleLogout is not a component).
Here are some alterations I would make, however I would suggest that you will need to make further changes based on the idea that you can use navigation params to pass information between screens. See more here https://reactnavigation.org/docs/en/params.html.
App.js
DefaultNavigationOptions can be a function which has a navigation prop, this is the navigation object you can use to get params or navigate in the context of the router.
remove that eslint exception because you don't need it, just properly declare the variable. Remove the "this" from you handleLogout function call because it is not a class attribute. Use navigation.getParam to get the isLoggedIn variable which you can pass in the navigate function call.
const handleLogout = navigation => {
firebase
.auth()
.signOut()
.then(() => navigation.navigate('Login'))
.catch(error => this.setState({errorMessage: error.message}));
}
...
defaultNavigationOptions: ({navigation}) => ({
headerRight: () => {
const isLoggedIn = navigation.getParam('isLoggedIn', false);
let button = isLoggedIn ? (
<Button
onPress={() => handleLogOut(navigation)}
title="Log-out"
color="#fff"
/>
) : ...
} ...
Now Loading.js
Here you need to add a navigation param to your navigate call which can then be used in the header
...
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.props.navigation.navigate('Main', {isLoggedIn: true});
} else {
this.props.navigation.navigate('SignUp', {isLoggedIn: false});
}
});
here is a modified version of your code in snack that you can see will get the logged in param and show the logout button https://snack.expo.io/#dannyhw/intrigued-graham-crackers
You will need to make further changes to fix other functionality because I only changed the minimum to show that you can use the navigation param.
So I am learning React-Native and I have created a Drawer which has already worked before. But after a few modifications not involving the Drawer it keeps giving this error Can't find variable: Contact even if I am not importing a variable called Contact.
drawer.js
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { DrawerNavigator, DrawerItems } from 'react-navigation';
import ButtonScreen from '../screens/ButtonScreen';
import Contacts from '../screens/Contacts';
import Information from '../screens/Information';
import Preferences from '../screens/Preferences';
const Drawer = new DrawerNavigator({
Home: {
screen: ButtonScreen,
navigationOptions: {
drawerLabel: 'Button'
},
},
Contacts: {
screen: Contacts,
},
Preferences: {
screen: Preferences,
},
Info: {
screen: Information,
}
}, {
contentComponent: (props) => (
<View>
<Text>Custom Header</Text>
<DrawerItems {...props}/>
<Text>Custom Footer</Text>
</View>
)
});
export default Drawer;
Contacts.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text } from 'react-native';
class Contacts extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
<Text>
Contact Component
</Text>
</View>
);
}
}
const mapStateToProps = (state) => {
const { isDrawerOpened } = state;
return { isDrawerOpened };
}
export default connect(mapStateToProps)(Contacts);
By the stacktrace the error is comming from drawer.js line 6, where I import Contacts and not Contact. I already runned npm start -- --reset-cache to see if it would solve it but no. I am very confuse about why this is happening.
I'm getting the following error (articulated above and noted in the screenshot here) for a React Native app. I'm implementing react-navigation into redux.
I also haven't put in any redirects into the app yet. I'm planning on calling NavigationActions to redirect to the Login screen based on the LoggedIn status which is being configured by a separate reducer that manages the state for the user.
The app was working properly when I did not have redux managing the navigation state. I decided to put the navigation state into redux when I realized I'll need some redirects based on the user logged in status which is being managed by the redux.
My code is as follows:
src/navigators/middleware.js
import {
createNavigationPropConstructor,
createReactNavigationReduxMiddleware,
} from 'react-navigation-redux-helpers';
// building redux utils for navigation
export const middleware = createReactNavigationReduxMiddleware(
'root',
state => state.nav,
);
export const navigationPropConstructor = createNavigationPropConstructor('root');
src/navigators/AppNavigator.js
import React from 'react';
import { StackNavigator } from 'react-navigation';
import Welcome from '../screens/Welcome';
import Dashboard from '../screens/Dashboard';
import Login from '../screens/Login';
routeNames = {
Welcome: { screen: Welcome },
Dashboard: { screen: Dashboard },
Login: { screen: Login },
};
config = {
navigationOptions: ({
header: 'null',
headerStyle: {
backgroundColor: 'white',
borderBottomWidth: 0,
},
headerLeft: null,
headerTitleStyle: {
fontSize: 30,
fontFamily: 'Roboto-Bold',
},
}),
};
export const AppNavigator = StackNavigator(routeNames, config);
src/navigators/AppWithInternalState.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addNavigationHelpers } from 'react-navigation';
import { AppNavigator } from './AppNavigator';
import { initializeListeners } from 'react-navigation-redux-helpers';
import { navigationPropConstructor } from './middleware';
class AppWithInternalState extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
nav: PropTypes.object.isRequired,
};
componentDidMount = () => {
initializeListeners('root', this.props.nav);
};
render = () => {
const { dispatch, nav } = this.props;
const navigation = navigationPropConstructor(dispatch, nav);
return <AppNavigator navigation={ navigation } />;
};
}
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(AppWithInternalState);
src/reducers/navReducers
import { AppNavigator } from '../navigators/AppNavigator';
import { NavigationActions } from 'react-navigation';
const router = AppNavigator.router;
const firstAction = router.getActionForPathAndParams('Dashboard');
const tempNavState = router.getStateForAction(firstAction);
const secondAction = router.getActionForPathAndParams('Login');
const initialNavState = router.getStateForAction(secondAction, tempNavState);
export default navReducer = (state=initialNavState, action) => {
let nextState;
switch (action.type) {
case 'Login':
nextState = router.getStateForAction(
NavigationActions.back(),
state,
);
break;
default:
nextState = router.getStateForAction(action.state);
break;
}
};
I was able to solve this myself. Looks like there is an issue with react-navigation when using createNavigationPropConstructor.
This will not be solved until react-navigation#2.03 is released.
Until then, create your own navigation prop:
import { createReduxBoundAddListener } from 'react-navigation-redux-helpers';
const navigation = {
dispatch,
state: nav,
addListener: createReduxBoundAddListener('root'),
};
You can see and track the issue here: https://github.com/react-navigation/react-navigation/issues/4320
My application contain drawer navigator and stack navigator. But issue is that when i tried to goback it takes me to first screen even if i am 2-3 stack deep. It don't show previous screen it always takes me to main screen. Below is my App.js code
import React from 'react';
import {
ActivityIndicator,
AsyncStorage,
Button,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import {StackNavigator, SwitchNavigator, DrawerNavigator} from 'react-navigation';
import Screen1 from './Screen/Screen1';
import Screen2 from './Screen/Screen2';
import Screen3 from './Screen/Screen3';
import Screen4 from './Screen/Screen4';
import Screen5 from './Screen/Screen5';
import ScreenList from './Screen/ScreenList';
import Login from './Screen/Login';
class AuthLoadingScreen extends React.Component {
constructor() {
super();
this._bootstrapAsync();
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('user');
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
this.props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
return (
<View style={styles.container}>
<ActivityIndicator/>
<StatusBar barStyle="default"/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
const AppStack = DrawerNavigator({
Screen1: { screen: Screen1},
Screen2: { screen: Screen2},
Screen3: { screen: Screen3},
Screen4: { screen: Screen4},
Screen5: { screen: Screen5},
ScreenList: { screen: ScreenList},
}, {contentComponent: SideBar});
const AuthStack = StackNavigator({Login: { screen: Login}},{headerMode:'none'});
const MyNavigator = SwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading'
}
);
export default class App extends React.Component {
render() {
return <MyNavigator />;
}
}
From Screen 1, i click on button and go to screen 2 and screen 3 like:
onPress={() => navigate('Screen2', { })}
And it works fine, but when i go back using bellow code from screen 3 it takes me to screen 1 not screen 2
this.props.navigation.goBack();
Am i missing something?
Did you try
1 -> 2 -> 3 -> 4
<Button
onPress={() => goBack('3')}
title="Go to 3 screen"
/>
I try to setup react-native-navigation but I tun in problem
My code is following
app.js
import React from 'react';
import { Provider } from 'react-redux'
import { Navigation } from 'react-native-navigation';
import registerScreens from './screens'
import configureStore from './src/store/configureStore';
export default class App extends React.Component {
constructor(props) {
super(props)
const store = configureStore()
registerScreens(store, Provider);
startApp()
}
startApp() {
Navigation.startSingleScreenApp({
screen: {
screen: 'ior.Login',
title: 'Welcome',
navigatorStyle: {},
navigatorButtons: {}
},
drawer: {} ,
passProps: {},
animationType: 'slide-down'
});
}
}
configureStore.js
import ReduxThunk from 'redux-thunk'
import reducers from '../reducers'
import { createStore, applyMiddleware } from 'redux';
export default function configureStore() {
return createStore(
reducers,
{},
applyMiddleware(ReduxThunk)
);
}
screens.js
import { Navigation } from 'react-native-navigation';
import Login from './src/components/Login'
export function registerScreens(store, Provider) {
Navigation.registerComponent('ior.Login', () => Login, store, Provider);
}
Login.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import { loginChanged, passwordChanged, loginUser } from '../actions';
class Login extends React.Component {
onLoginChange(text) {
this.props.loginChanged(text)
}
onPasswordChange(text) {
this.props.passwordChanged(text)
}
onPress() {
this.props.loginUser(this.props.email, this.props.password)
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.textField}
onChangeText={this.onLoginChange.bind(this)}
value={this.props.email}
placeholder={"Логин"}
/>
<TextInput
style={styles.textField}
onChangeText={this.onPasswordChange.bind(this)}
value={this.props.password}
placeholder={"Пароль"}
/>
<Button onPress={this.onPress.bind(this)} title={"Войти"}/>
</View>
)
}
}
const styles = StyleSheet.create({
textField: {
flex: 1,
width: '80%',
height: 100,
},
container: {
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
flex: 0.3,
marginTop: 210
},
button: {
width: '100%'
}
});
const mapStateToProps = state => {
return {
email: state.login.email,
password: state.login.password
}
}
export default connect(mapStateToProps, { loginChanged, passwordChanged, loginUser })(Login)
actions/index.js
import axios from 'axios'
export const loginChanged = (text) => {
return {
type: 'LOGIN_CHANGED',
payload: text
}
}
export const passwordChanged = (text) => {
return {
type: 'PASSWORD_CHANGED',
payload: text
}
}
export const loginUser = (email, password) => {
const creds = { mail: email, password: password }
console.log(creds)
return (dispact) => {
axios.post('http://192.168.1.10:3000/users/auth', creds)
.then(response => console.log(response))
.catch( err => console.log(err))
}
}
What am I doing wrong ?
The issue is that registerScreens is exported as a named export but you're importing it as a default export.
import registerScreens from './screens'
// ^ this is how you import a default export
You should add export default to registerScreens
export default function registerScreens(store, Provider) {
// ...
}
Or import it like a named export:
import { registerScreens } from './screens'
Additionally, you are calling a class method startApp() as if it was a normal function but it's a method on your class.
export default class App extends React.Component {
constructor(props) {
// ...
startApp()
// ^calling like a simple function
}
}
You must call it from the context:
export default class App extends React.Component {
constructor(props) {
// ...
this.startApp()
// ^calling it like a method
}
}