Google SignIn with expo: signInWithGoogleAsync not being invoked - react-native

Im following this tutorial https://github.com/nathvarun/Expo-Google-Login-Firebase and below is my code. However the signInWithGoogleAsync() function is not getting invoked it seems. Clicking the button does nothing. Could it be because of stack Navigator?
There is no issue with firebase as email login is working
Below is my LoginScreen.js
import { useNavigation } from '#react-navigation/core'
import React, { useEffect, useState, Component } from 'react'
import { Button, KeyboardAvoidingView, StyleSheet, Text,TextInput, TouchableOpacity, View } from 'react-native'
import { auth } from '../firebase'
import * as GoogleSignIn from 'expo-google-sign-in';
class LoginScreen extends Component {
signInWithGoogleAsync = async() => {
try {
const result = await Google.logInAsync({
behavior:'web',
iosClientId: 'lorem ipsum',
scopes: ['profile', 'email'],
});
if (result.type === 'success') {
return result.accessToken;
} else {
return { cancelled: true };
}
} catch (e) {
return { error: true };
}
};
render(){
return (
<KeyboardAvoidingView
style={styles.container}
behavior="padding"
>
<View style={styles.inputContainer}>
<Button
title='sign'
onPress={()=>this.signInWithGoogleAsync()} />
</View>
</KeyboardAvoidingView>
);
}
}
export default LoginScreen
here's the App.js:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from './screens/LoginScreen';
import HomeScreen from './screens/HomeScreen';
import * as firebase from "firebase/compat";
const Stack = createNativeStackNavigator();
export default class App extends React.Component {
render(){
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen options={{ headerShown: false }} name="Login" component={LoginScreen} />
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
registerRootComponent(App);

You are importing everythin as GoogleSignIn and then below doing await Google.logInAsync so you'd have to change that to await GoogleSignIn.loadInAsync. 9th line
Also, look at the docs, it doesn't look like logInAsync is a function https://docs.expo.dev/versions/latest/sdk/google-sign-in/

expo-google-sign-in has been now deprecated. You'll have to use expo-auth-session

Related

How to solve: TypeError: undefined is not an object (evaluating 'navigation.navigate')

Im trying to use Stack navigator in react native in order to go from my "welcome screen" to my next page, "create alarm screen" However, it keeps giving me the error that navigation.navigate is undefined.
I created a MyStack function in an index.js file:
const Stack = createNativeStackNavigator();
function MyStack() {
return (
<Stack.Navigator initialRouteName='WelcomeScreen'>
<Stack.Screen name="WelcomeScreen" component={WelcomeScreen} />
<Stack.Screen name="CreateAlarmScreen" component={CreateAlarmScreen} />
</Stack.Navigator>
);
}
export default MyStack;
This is my WelcomeScreen.js file:
import React, { useEffect, useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Dimensions,Button, Text, View ,TouchableOpacity, Image, SafeAreaView, Component } from 'react-native';
import { backgroundColor } from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function WelcomeScreen({navigation}) {
return (
<SafeAreaView style= {styles.Container}>
<View style={styles.CreateAlarmButton}>
<Button
onPress={()=>navigation.navigate('CreateAlarmScreen')}
title='Create an Alarm'
color="white"
/>
</View>
<View style={styles.ViewAlarmButton}>
<Button
title='View Alarms'
color="black"
/>
</View>
</SafeAreaView>
);
}
export default WelcomeScreen;
and then this is my app.js file:
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Dimensions, Text, View ,TouchableOpacity, Image, SafeAreaView } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import WelcomeScreen from './app/screens/WelcomeScreen';
import CreateAlarmScreen from './app/screens/CreateAlarmScreen';
import MyStack from './app/Navigation';
export default function App() {
return (
<NavigationContainer>
<MyStack/>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'peachpuff',
justifyContent: 'center',
alignItems: 'center'
},
});
I really confused on how to fix this issue.
you can import use navigation, and then you can declare that variable
import { useNavigation } from '#react-navigation/native';
const navigation = useNavigation();
navigation is often populated after the first render, so it will be undefined until it populates. Try navigation?.navigate instead.

How to pass "this.state" of a Component to a Screen in React Navigation 5

I am new to React Native and I just started learning ReactNavigation.
The app that I'm working at is meant to keep track of players and points while you're playing a board game. Here's the code of App.js:
import * as React from 'react';
import {Component} from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { create StackNavigator } from '#react-navigation/stack';
import { Player } from './player';
const Stack = createStackNavigator();
export default class BoardGameApp extends Component{
constructor(props){
super(props);
this.state={ playerTable:[] }
}
render(){
let numOfPlayers = 4
const totNumOfTerritories = 48
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
function HomeScreen(){
return(
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Add a new Player" onPress={
() => this.setState({ playerTable: [
...this.state.playerTable,
{ playerName: "whatever"},
]
})}>
</Button>
/* this is commented
{this.state.playerTable.map(
function(player) {
return <Player territories={totNumOfTerritories / numOfPlayers} />;
}
)} here the comment ends */
</View>
);
}
So basically what should happen is that whenever I press the Button, an object consisting of {playerName: "whatever"} should be added to the PlayerTable[] array present in the state of BoardGameApp, and then what I subsequently do is that I map over this array and render a Player component for each object {playerName: "whatever"} present in the array. When all of this was happening inside the App component without using screens everything was fine, but of course it looked awful.
Once I started using screens however, when I press the Button on my phone it gives me the error undefined is not an object (evaluating '_this2.setState') because I guess it doesn't recognize what "this" is.
I am learning ReactNative by following the Cs50 course 2018 where "ScreenProps" were still available. I made some research and I think that my answer should be somewhere around Initial Params, React Contextor Redux even if I still don't know these concepts, however whenever I try to look up on the docs or on Youtube, it just gives me way more complicated stuff than just passing a state from an object to a screen. So this is why I ultimately decided to ask this question here.
Working Example: Expo Snack
Console Output:
You can solve it using Context API:
import React, { Component, createContext, useContext } from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
// import { Player } from './player';
const Stack = createStackNavigator();
const ParentContext = createContext();
export default class BoardGameApp extends Component {
constructor(props) {
super(props);
this.state = { playerTable: [] };
}
addPlayer = (player) => {
console.log('data added :', [...this.state.playerTable, player]);
this.setState({
playerTable: [...this.state.playerTable, player],
});
};
render() {
let numOfPlayers = 4;
const totNumOfTerritories = 48;
return (
<ParentContext.Provider value={this.addPlayer}>
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
</ParentContext.Provider>
);
}
}
function HomeScreen() {
const addPlayer = useContext(ParentContext);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Add a new Player"
onPress={() => {
addPlayer({ playerName: 'whatever' });
}}></Button>
</View>
);
}
const styles = StyleSheet.create({});
Old solution which was not the right way according to comment given by #satya so avoid it but I am keeping it in answer for future ref:
import * as React from 'react';
import { Component } from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
// import { Player } from './player';
const Stack = createStackNavigator();
export default class BoardGameApp extends Component {
constructor(props) {
super(props);
this.state = { playerTable: [] };
}
addPlayer = (player) => {
console.log('hi:', [...this.state.playerTable, player]);
this.setState({
playerTable: [...this.state.playerTable, player],
});
};
render() {
let numOfPlayers = 4;
const totNumOfTerritories = 48;
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={() => <HomeScreen onClick={this.addPlayer} />}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
function HomeScreen({ onClick }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Add a new Player"
onPress={() => {
onClick({ playerName: 'whatever' });
}}></Button>
</View>
);
}
const styles = StyleSheet.create({});

React native: How to import a functional component if it is not exported as default

I'm not able to move the SignIn screen from the index.js file to Screens.js file
I have been following the default react-navigation documentation and after getting the first example up an running I renamed the HomeScreen to SignIn. This worked. The second step for me was to move the SignIn screen from index.js to Screens.js
This works
index.js
import * as React from "react";
import { View, Text } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
//import { SignIn } from "./Screens";
export const SignIn = () => {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Sign in</Text>
</View>
);
}
const AuthStack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<AuthStack.Navigator>
<AuthStack.Screen name="SignIn" component={SignIn} />
</AuthStack.Navigator>
</NavigationContainer>
);
}
export default App;
Screens.js
import Rreact from "react";
import { View, Text } from "react-native";
export const SignIn = () => {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Sign in</Text>
</View>
);
};
This does not work
I'm trying to use the SignIn component from the Screens.js file like so
import * as React from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import { SignIn } from "./Screens";
const AuthStack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<AuthStack.Navigator>
<AuthStack.Screen name="SignIn" component={SignIn} />
</AuthStack.Navigator>
</NavigationContainer>
);
}
export default App;
This is the error
Could someone explain to me what am I missing?
In Screens.js change the following line
import Rreact from "react";
to
import React from "react";

React-native stack navigation not showing right screen

all, I am having a problem that my stack navigation is not showing any screen.
I have 'signup' and 'login' screens, and I build a stack navigator(which is the startStack) try to navigate between those two screens. But it doesn't work. when a user first come(if she hasn't logged in before), the 'login' screen should be shown.
Please help me out , I really appreciate , thank you.
this is the App.js
import React, { Component } from "react";
import "react-native-gesture-handler";
import { StyleSheet, Text, View } from "react-native";
import Login from "./screens/login";
import Signup from "./screens/signup";
import MyOrders from "./screens/myOrders";
import firebase from "firebase";
import StartNavigator from "./routes/startStack";
class App extends Component {
state = {
loggedIn: null,
};
componentDidMount() {
var firebaseConfig = {
apiKey: "AIzaSyASR4GAXSRGsiDhOTF_UsdqHzqHYcHPk_U",
authDomain: "onlineorderportal-68a8f.firebaseapp.com",
databaseURL: "https://onlineorderportal-68a8f.firebaseio.com",
projectId: "onlineorderportal-68a8f",
storageBucket: "onlineorderportal-68a8f.appspot.com",
messagingSenderId: "658149255251",
appId: "1:658149255251:web:37940844cdc5403e173ea6",
measurementId: "G-KGQ1F2F3WE",
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ loggedIn: true });
} else {
this.setState({ loggedIn: false });
}
});
}
}
renderContent = () => {
switch (this.state.loggedIn) {
case false:
console.log("false case login");
// return <Login />;
// return <Signup />;
return <StartNavigator />;
case true:
console.log("true case Navigator");
return <MyOrders />;
}
};
render() {
return (
<View style={styles.container}>
{this.renderContent()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#FFFFFF",
alignItems: "center",
justifyContent: "center",
},
});
export default App;
This is the startStack.js
import React from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
import Login from "../screens/login";
import Signup from "../screens/signup";
const Stack = createStackNavigator();
function StartNavigator() {
console.log("enter Start");
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="Signup" component={Signup} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default StartNavigator;
When I try to return 'Login' or 'Signup' separately, it correctly show the pages separately.
Thank you so much !!!
You have created StartNavigator, which has only two screens 'Login' and 'Singup'. And you have set initialRouteName as 'Login'. So your 'App' component never called. So all the code that you written on App component never run. To work this add App component in your StartNavigator and set initialRouteName to app like below :
import React from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
import Login from "../screens/login";
import Signup from "../screens/signup";
import App from "../app/path"; // import App here
const Stack = createStackNavigator();
function StartNavigator() {
console.log("enter Start");
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="App" component={App} /> // add app component
<Stack.Screen name="Signup" component={Signup} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default StartNavigator;
Did you check conditions after receive data from the server?
what is the value of 'loggedIn'?
I think that the value of 'loggedIn' is not correct or your switch case is not working.
use console.log before doing any condition for sure that the value of 'loggedIn' is correct.

Login component react-native

I am making a react-native application.
I am trying to find out how can I show or hide the login component on the basis of a token stored. If the token is present in the storage, it would skip the login screen to the dashboard.
Else it will show login screen.
Here is the code to the app.
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, AsyncStorage } from 'react-native';
import Login from './components/Login';
import Dashboard from './components/Dashboard';
export default function App() {
var [ token, setToken ] = useState('');
function loginHandler(recievedToken) {
setToken(recievedToken);
}
async function _readData() {
try {
const value = await AsyncStorage.getItem('Token');
console.log(value);
if (value !== null) {
// We have data!!
loginHandler(value)
} else {
loginHandler(null);
}
} catch (error) {
// Error retrieving data
}
};
return (
<View>
<Login getToken={_readData()} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
You can put conditional statement in your code.
Try this code out.
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, AsyncStorage, ActivityIndictor } from 'react-native';
import Login from './components/Login';
import Dashboard from './components/Dashboard';
export default function App() {
var [ token, setToken ] = useState('');
var [ loading, setLoading ] = useState(true);
function loginHandler(recievedToken) {
setToken(recievedToken);
setLoading(false);
}
async function _readData() {
try {
const value = await AsyncStorage.getItem('Token');
console.log(value);
if (value !== null) {
// We have data!!
loginHandler(value)
} else {
loginHandler(null);
}
} catch (error) {
// Error retrieving data
}
};
if( loading ) {
<ActivityIndictor/>
}
if(token == null){
return (
<View>
<Login getToken={_readData()} />
</View>
);
} else {
return (
<View>
<Dashboard />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Let me know if some error or correction.
Here AuthStack is Login Screen and BottomNavigation is DashBoard.
import React, { useEffect, useState } from "react";
import { NavigationContainer } from "#react-navigation/native";
import AuthStackScreen from "./routes/AuthStack";
import BottomNavigation from "./routes/BottomNavigator";
import firebase from "./config/firebase";
export default App = () => {
const [isLogin, setIsLogin] = useState(false);
const checkLoginIn = () => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
setIsLogin(true);
} else {
setIsLogin(false);
}
});
};
useEffect(() => {
checkLoginIn();
}, []);
return (
<NavigationContainer>
{isLogin ? <BottomNavigation /> : <AuthStackScreen />}
</NavigationContainer>
);
};
In my case
I just used initialRouteName
import React, {Component} from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createNativeStackNavigator} from '#react-navigation/native-stack';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Home from './components/Home';
import Login from './components/Login';
const Stack = createNativeStackNavigator();
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
routeName: 'Login',
};
}
async componentDidMount() {
const token = JSON.parse(await AsyncStorage.getItem('token'));
if (token !== null) {
this.setState({
routeName: 'Bluetooth',
});
}
SplashScreen.hide();
}
render() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={this.state.routeName}
screenOptions={{
headerShown: false,
}}>
<>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Home" component={Home} />
</>
</Stack.Navigator>
</NavigationContainer>
);
}
}