How to pass navigation props in React native in Stack Navigation? - firebase

I am new to react native and facing some issues with React Navigation version 5.x
App.js file is below
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import MyDrawer from "./components/MyDrawer";
import LoginScreen from "./screens/LoginScreen";
import firebase from "firebase";
import { firebaseConfig } from "./config";
firebase.initializeApp(firebaseConfig);
const Stack = createStackNavigator();
export default class App extends React.Component {
state = {
isLoggedIn: false,
};
logOut = () => {
this.setState({ isLoggedIn: false });
firebase.auth().signOut();
};
render() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false,
}}
>
{this.state.isLoggedIn ? (
<>
<Stack.Screen name="Home" component={MyDrawer} />
</>
) : (
<Stack.Screen name="SignIn" component={LoginScreen} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
}
If the user is logged in to firebase it will navigate to MyDrawer.js. It has a custom drawer and the drawer has a log out button.
MyDrawer.js
import HomeScreen from "../screens/HomeScreen";
import Colors from "../Colors";
import ShareListScreen from "../screens/ShareListScreen";
import Inpiration from "../screens/Inspiration";
const Drawer = createDrawerNavigator();
export default class MyDrawer extends React.Component {
state = {
title: this.props,
};
render() {
return (
<Drawer.Navigator
initialRouteName="Home"
drawerContent={(props) => <CustomDrawerContent {...props} />}
>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Share List" component={ShareListScreen} />
<Drawer.Screen name="Inspiration" component={Inpiration} />
</Drawer.Navigator>
);
}
}
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<View >
<ImageBackground source={image} >
<Text>
Bring
<Text>Me</Text>
</Text>
</ImageBackground>
</View>
<DrawerItemList {...props} />
<ImageBackground source={home} >
<Text >Home</Text>
</ImageBackground>
<ImageBackground source={work} >
<Text>Workplace</Text>
</ImageBackground>
<DrawerItem
label="Log out"
onPress={() => {
this.props.logOut;
this.props.navigation.navigate("SignIn");
// Once the button pressed I want the user to sign out from firebase and navigate
to the root and set isLoggedIn state to true in App.js.
}}
/>
</DrawerContentScrollView>
);
}
Once the logout button pressed I want the user to sign out from firebase and navigate to the root and set isLoggedIn state to true in App.js. ( Call the log out function in App.js).
How can I achieve this?

You can use the initialParams to pass the logout function.
<Stack.Screen name="Home" component={MyDrawer} initialParams={{this.logout}}/>
This function can be accessed in MyDrawer class as this.props.routes.params.logout()
And you dont have to navigate to signIn again as the state change happens you render the 'SignIn' so app will show the signin screen automatically.

Related

auth/invalid-value-(email),-starting-an-object-on-a-scalar-field

This is a hot problem I see a lot of people asking it about it in the last couple of days and its not even registered in Firebase Documentations.
Operating System: OS 11.6.1
React-Native
expo-cli 6.0.5
Firebase #9.2.0
Starting with imports ON the actual
[Prettier Code][1]
[1]: https://i.stack.imgur.com/5UfNQ.png
Initialising Firebase (Back-end)
[Prettier Code][2]
[2]: https://i.stack.imgur.com/UZTUu.png
Initializing Auth, and CreateUserWithEmailAndPassword (Back-end)
[Prettier Code][3]
[3]: https://i.stack.imgur.com/9eZdn.png
Form TextInput and Button
[Prettier Code][FORM]
[FORM]: https://i.stack.imgur.com/5Xom9.png
useState and registering Handler { logger() }
[Prettier Code][5]
[5]: https://i.stack.imgur.com/xHDBd.png
LOGS: Firebase: Error (auth/invalid-value-(email),-starting-an-object-on-a-scalar-field-invalid-value-(password),-starting-an-object-on-a-scalar-field).
import { View, Text, Button, TextInput, KeyboardAvoidingView, Keyboard } from 'react-native'
import React, { useState } from 'react'
import styles from '../../Components/Card/Comments/styles'
import { initFirebase, auth, registerUserEmlPwd } from '../../../firebase'
export default function RegisterScreen({navigation}) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
function logger (){
initFirebase();
registerUserEmlPwd(auth ,email, password )
console.log(email, password);
}
return (
<KeyboardAvoidingView>
<View
style={{
marginTop: 100,
justfiyContent: 'center',
alignItems: 'center',
}}
>
<Text>Register</Text>
<TextInput
placeholder="Email"
returnKeyType="next"
value={email.value}
onChangeText={(text) => setEmail(text)}
error={!!email.error}
errorText={email.error}
autoCapitalize="none"
autoCompleteType="email"
textContentType="emailAddress"
keyboardType="email-address"
/>
<TextInput
placeholder="Password"
returnKeyType="done"
value={password.value}
onChangeText={(text) => setPassword(text)}
error={!!password.error}
errorText={password.error}
secureTextEntry
/>
<Button title="Register" onPress={logger} />
<Button
title="Go Back"
onPress={() => {
navigation.goBack();
}}
/>
</View>
</KeyboardAvoidingView>
);
}
enter code here

React Native : Navigate to a screen "nested inside BottomTabNavigator" from firebase push notificaiton click

This is a part of my BottomNavigator code in App.js.
const Bottom = createBottomTabNavigator();
return (
<Bottom.Navigator
tabBarOptions={...}>
<Bottom.Screen
name="ScreenA"
component={ScreenA}
options={...}
/>
<Bottom.Screen
name="ScreenB"
component={ScreenB}
options={...}
/>
<Bottom.Screen
name="ScreenC"
component={ScreenC}
options={...}
/>
<Bottom.Screen
name="Chat"
component={Chat}
options={({navigation}) => ({
tabBarLabel: StringsOfLanguages.chat,
tabBarIcon: ({focused, color, size}) =>
focused ? (
<Image
style={appStyles.bottomTabImgSize}
source={require('./assets/abc.svg')}
/>
) : (
<Image
style={appStyles.bottomTabImgSize}
source={require('./assets/def.svg')}
/>
),
tabBarButton: (props) =>
<TouchableOpacity
{...props}
onPress={() =>
navigation.navigate('Chat', {screen: 'ChatSubA'})
}
/>
})}
/>
</Bottom.Navigator>
);
and this is the code for bottom tab named "Chat"
const Chat = () => {
// Usually ChatSubB is called from ChatSubA.. But on receiving push notification
// ChatSubB should be directly loaded.
<Stack.Navigator initialRouteName="ChatSubA">
<Stack.Screen
name="ChatSubA"
component={ChatSubA}
options={{headerShown: false}}
/>
<Stack.Screen
name="ChatSubB"
component={ChatSubB}
options={{headerShown: false}}
/>
<Stack.Screen
name="ChatSubC"
component={ChatSubC}
options={{headerShown: false}}
/>
<Stack.Screen
name="ChatSubD"
component={ChatSubD}
options={{headerShown: false}}
/>
</Stack.Navigator>
);};
Say if I want to navigate to 'ChatSubB' screen from ScreenA/ScreenB/ScreenC I am using the code
props.navigation.navigate(Chat, {
screen: ChatSubB,
params: {param1:'hai'},
});
But now I need to call 'ChatSubB' on push notification onclick
I don't have 'props' or 'navigate' available to call the above line of code.
This is my PushNotificationHelper.js file. I call these methods from App.js useEffect()
import messaging from '#react-native-firebase/messaging';
import AsyncStorage from '#react-native-async-storage/async- storage';
export async function requestUserPermission() {
...
await getFcmToken();}
export async function getFcmToken() {
...}
export const notificationListener = (navigate) => {
messaging().onNotificationOpenedApp(remoteMessage => {
console.log(
'Notification caused app to open from background state:',
remoteMessage.notification,
);
// navigate("ChatScreen",{
// result: "2371820992-5406-07082-13972-17488760826513",
// });
// navigate('ChatScreen', {
// result: "2371820992-5406-07082-13972-17488760826513",
// });
navigate("Others", {
screen: ChatScreen,
params: {result: "2371820992-5406-07082-13972-17488760826513"},
});
});}
While refering to obtain props or navigate I found a solution using createRef
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
and use
RootNavigation.navigate('ChatSubB ', {param1:'hai'})
But this code doesn't work for me as "ChatSubB" is nested in BottomTabNavigator
tab "Chat".
Is there any other solution to achieve my requirement?
Any help would be grateful..
As explained in the docs you can navigate like this -
RootNavigation.navigate('ChatSubB', {
screen: 'Chat',
params: {
param1:'hai'
}
});
Maybe this documentation may help you?
https://reactnavigation.org/docs/nesting-navigators/#nesting-multiple-navigators
And try to use useNavigation hook)

'NAVIGATE' with payload {"name":"LoginStack","params":{"screen":"Login"}} was not handled by any navigator

I have an error when navigating from Home to Login Screen when the user press the button Sign out.
Here is the App component:
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import { LoginScreen } from "./Screens/LoginScreen";
import { HomeScreen } from "./Screens/HomeScreen";
import { SignUpScreen } from "./Screens/SignUpScreen";
import { CardScreen } from "./Screens/CardScreen";
import { useEffect, useState } from "react";
import { auth } from "./firebase/firebase";
const Stack = createNativeStackNavigator();
export default function App() {
const [user, setUser] = useState(null);
useEffect(() => {
auth.onAuthStateChanged((user) => {
if (user !== null) setUser(user);
});
}, []);
const LoginStack = () => {
return (
<>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="SignUp"
component={SignUpScreen}
options={{ headerShown: false }}
/>
</>
);
};
return (
<NavigationContainer>
<Stack.Navigator>
{user ? (
<>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen name="CardScreen" component={CardScreen} />
</>
) : (
<Stack.Screen name="LoginStack" component={LoginStack} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
Here is the Home component:
import { MaterialCommunityIcons } from "#expo/vector-icons";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { signOut } from "firebase/auth";
import { Button } from "react-native";
import { auth } from "../firebase/firebase";
import { DashboardScreen } from "./DashboardScreen";
import { SettingsScreen } from "./SettingsScreen";
const Tab = createBottomTabNavigator();
const HomeScreen = ({ navigation }) => {
const handleSignout = () => {
signOut(auth)
.then(() => {
console.log("Sign out");
navigation.navigate("LoginStack", { screen: "Login" });
})
.catch((error) => {
console.log("Error", error);
});
};
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === "Dashboard") {
iconName = focused ? "pin" : "pin-outline";
} else if (route.name === "Settings") {
iconName = focused ? "account-circle" : "account-circle-outline";
}
// You can return any component that you like here!
return (
<MaterialCommunityIcons name={iconName} size={size} color={color} />
);
},
tabBarActiveTintColor: "#95E0B6",
tabBarInactiveTintColor: "gray",
})}
>
<Tab.Screen
name="Dashboard"
component={DashboardScreen}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
headerRight: () => (
<Button onPress={handleSignout} title="Sign out" />
),
}}
/>
</Tab.Navigator>
);
};
export { HomeScreen };
I'm following the instructions described here related to the nested navigator but seems to not work: https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator
Any idea about how can I navigate from the Home Screen to the Login Screen?
Thanks
Your screen stack constructor depends on user variable.
You can't navigate to a stack that is not built, but you can set the user = null and the application will render automatically to LoginStack.
The best practice to manage user signIn/signOut is to put the user state in a context.

Can't change variable when using Firebase onAuthStateChanged

I'm writing an app in React Native and I'm trying to make the app change screen automatically depending on if the user is logged in or not.
Everything works except that the variable isSignedIn is undefined so what I need help with is how to define it inside onAuthStateChanged.
Thankful for help!
Here is my code:
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import React, { useEffect, useState } from 'react';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
import { useFonts } from 'expo-font';
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { auth } from './app/config/firebase';
import IntroSliderScreen from './app/screens/introSlider';
import FirstScreenScreen from './app/screens/firstScreen';
import LogInScreen from './app/screens/logIn';
import OverrideScreen from './app/screens/override';
import SignUpScreen from './app/screens/signUp';
import HomeScreen from './app/screens/mainTabs/home';
import KartaScreen from './app/screens/mainTabs/karta';
import MatScreen from './app/screens/mainTabs/mat';
import MoreScreen from './app/screens/mainTabs/more';
import ProgramScreen from './app/screens/mainTabs/program';
import Push from './app/config/push';
const Stack = createNativeStackNavigator();
const Tab = createMaterialBottomTabNavigator ();
function MainTabs() {
return(
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Program" component={ProgramScreen} />
<Tab.Screen name="Karta" component={KartaScreen} />
<Tab.Screen name="Mat" component={MatScreen} />
<Tab.Screen name="More" component={MoreScreen} />
</Tab.Navigator>
);
}
export default function App() {
const [loaded] = useFonts({
PirataOne: require('./app/assets/fonts/PirataOne-Regular.ttf'),
RobotoMono: require('./app/assets/fonts/RobotoMono-VariableFont_wght.ttf'),
});
if (!loaded) {
return null;
};
var isSignedIn;
//const [isSignedIn, setIsSignedIn] = useState(false);
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
console.log('Inloggad med UserID: ' + uid);
//setIsSignedIn(true);
} else {
// User is signed out
// ...
console.log('Ej inloggad');
}
});
return (
<NavigationContainer>
{isSignedIn ? (
<Stack.Navigator>
<Stack.Screen name='MainTabs' component={MainTabs} />
<Stack.Screen name="IntroSliderScreen" component={IntroSliderScreen} />
</Stack.Navigator>
) : (
<Stack.Navigator>
<Stack.Screen options={{headerShown: false}} name="FirstScreenScreen" component={FirstScreenScreen} />
<Stack.Screen name="SignUpScreen" component={SignUpScreen} />
<Stack.Screen name="LogInScreen" component={LogInScreen} />
</Stack.Navigator>
)}
</NavigationContainer>
);
}
/* <Stack.Group navigationKey={isSignedIn ? 'user' : 'guest'}>
<Stack.Screen name="OverrideScreen" component={OverrideScreen} />
<Stack.Screen name="Push" component={Push} />
</Stack.Group> */

Navgigation.navigate(param) with PushNotification in react native and react navigation

I want to redirect my user on the page I choose when they click on my push notification. I know how to get parameters from firebase.
Push Notification package : https://github.com/zo0r/react-native-push-notification
React navigation : https://reactnavigation.org/docs/getting-started/
Index.tsx :
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import PushNotification from "react-native-push-notification";
import { useNavigation } from '#react-navigation/native';
PushNotification.configure({
onNotification: function (notification) {
console.log(notification)
screenToRedirect = notification.data.screen //from firebase key => value
//here I'd like to do something like this :
const navigation = useNavigation();
navigation.navigate(screenToRedirect )
},
requestPermissions: Platform.OS === 'ios'
});
AppRegistry.registerComponent(appName, () => App);
It tells me:
Invalid hook call. Hooks can only be called inside of the body of a function component. Problem : I can't put the PushNotification.configure inside a component (it is mentioned in the doc).
You can't use hooks outside react components. You can try this solution: https://reactnavigation.org/docs/navigating-without-navigation-prop/
SOLUTION
I just added my principal menu in the root navigation. So the navigationRef return my principal menu, example :
export const Drawer = createDrawerNavigator();
export const MyDrawer = () => {
return(
<Drawer.Navigator initialRouteName="Accueil">
<Drawer.Screen name="Accueil" component={Home} />
<Drawer.Screen name="Qui sommes-nous ?" component={Who} />
<Drawer.Screen name="Nos Services" component={Services} />
<Drawer.Screen name="Nos Biens" component={Bien} />
</Drawer.Navigator>
)
}
export const Bottom = createBottomTabNavigator();
export const MyBottom = () => {
return(
<Bottom.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'home'
: 'folder';
} else if (route.name === 'Mon Profil') {
iconName = focused ? 'alert-circle' : 'aperture-sharp';
}
// You can return any component that you like here!
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}
>
<Bottom.Screen name="Home" component={MyDrawer} options={{ headerShown: false }}/>
<Bottom.Screen name="Mon Profil" component={Profil} options={{ headerShown: false }}/>
</Bottom.Navigator>
)
}
export const navigationRef = createNavigationContainerRef()
export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
const RootStack = createNativeStackNavigator();
export default function AppMenu() {
return (
<NavigationContainer ref={navigationRef} independent={true}>
<RootStack.Navigator initialRouteName="Accueil">
// Here, I return My Bottom which is the principal menu that return itself MyDrawer menu
<RootStack.Screen name="Home2" component={MyBottom} options={{ headerShown: false }}/>
</RootStack.Navigator>
</NavigationContainer>
);
}
And now My App.tsx return AppMenu.
Here's how I solved this problem:
1- First create a RootNavigation,js file
2- Add the below code in that file
import {createNavigationContainerRef} from '#react-navigation/native';
export const navigationRef = createNavigationContainerRef();
export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
3- Add navigationRef in navigation container
import {navigationRef} from './RootNavigation';
<NavigationContainer ref={navigationRef}>
<MainStack />
</NavigationContainer>
4- Now you can use this ref outside of NavigationContainer as well if you have initialized your push notifications in app.js
import * as RootNavigation from './src/navigation/RootNavigation';
***if you are using one signal for push notifications then you can navigate to your screen in setNotificationOpenedHandler method***
//Method for handling notifications opened
OneSignal.setNotificationOpenedHandler(notification => {
RootNavigation.navigate("your_screen_name");
});

Resources