So I'm building an app where i send notification to the user when there's an update. Once the user receives it, on clicking, I want to navigate to a specific screen in my app.
The problem is, to do that I have to use a navigation reference to be able to access the navigation object. I did read the doc, but once applying it I get the following error:
The action 'NAVIGATE' with payload {"name":"HomeScreen","params":{"userName":"Lucy"}} was not handled by any navigator.
Do you have a screen named 'HomeScreen'?
RootNavigation.js:
import * as React from 'react';
import { createNavigationContainerRef } from '#react-navigation/native';
export const navigationRef = createNavigationContainerRef()
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
}
App.js:
import React, { useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import AppNavigator from "./app/navigation/AppNavigator";
import { NavigationContainer } from "#react-navigation/native";
import {navigationRef} from "./app/navigation/rootNavigation";
export default function App() {
return (
<AuthContext.Provider>
<NavigationContainer ref={navigationRef}>
<AppNavigator />
</NavigationContainer>
</AuthContext.Provider>
);
}
AppNavigator.js:
import React, {useEffect, useContext} from "react";
import HomeScreen from "../screens/Home/HomeScreen";
import DashboardScreen from "../screens/Home/DashboardScreen";
import { createDrawerNavigator } from "#react-navigation/drawer";
import FolderNavigator from "./FolderNavigator";
import * as Notifications from "expo-notifications";
import * as RootNavigation from "./rootNavigation";
import AuthNavigator from "./AuthNavigator";
const Drawer = createDrawerNavigator();
const AppNavigator = ({}) => {
useEffect(() =>{
registerForPushNotifications()
Notifications.addNotificationReceivedListener(notification =>{
RootNavigation.navigate('HomeScreen', { userName: 'Lucy' });
})
},[])
const registerForPushNotifications = async ( ) => {
try {
const permissions = await Notifications.getPermissionsAsync();
if (!permissions.granted) return;
/*const token = await Notifications.getExpoPushTokenAsync()*/
const token = (await Notifications.getExpoPushTokenAsync()).data;
/*await expoPushTokenApi.register(token, user.guid)*/
/*console.log(token)*/
} catch (error){
console.log(error)
}
};
return (
<Drawer.Navigator>
<Drawer.Screen
name="Dashboard"
component={DashboardScreen}
/>
<Drawer.Screen
name="Liste des dossiers"
component={FolderNavigator}
/>
<Drawer.Screen
name="Liste des clients"
component={HomeScreen}
/>
</Drawer.Navigator>
)};
export default AppNavigator;
FolderNavigator.js:
import React from "react";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import HomeScreen from "../screens/Home/HomeScreen";
import FolderDetails from "../screens/FolderDetails";
const Stack = createNativeStackNavigator();
const FolderNavigator = () => (
<Stack.Navigator>
<Stack.Screen
name="Liste des dossiers"
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Détails"
component={FolderDetails}
options={{ headerTitle: "", headerBackTitle: "" }}
/>
</Stack.Navigator>
);
export default FolderNavigator;
Directory structure:
This line RootNavigation.navigate('HomeScreen', { userName: 'Lucy' }) is causing the error, because when setting up your navigation, you didn't name any screen HomeScreen, I'm talking about Stack.Screen's name property and Drawer.Screen's name property.
Related
I want to insert a header button for user to be able to sign out easily but I am having trouble with it.
I am referring to https://reactnavigation.org/docs/header-buttons
Within my app.js
import { StatusBar } from 'expo-status-bar';
import React from 'react'
import { signOut } from 'firebase/auth';
import { auth } from '../firebase';
import { StyleSheet, Text, View, Button } 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 RegisterScreen from './screens/RegisterScreen';
import ForgetPasswordScreen from './screens/ForgetPasswordScreen';
import SubScreen1 from './screens/SubScreen1';
const Stack = createNativeStackNavigator();
const handleSignOut = async () =>{
try{
await signOut(auth)
console.log("Signed out successfully")
navigation.replace("Login")
}catch (error) {
console.log({error});
}
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen options= {{ headerShown : false }} name="Login" component={LoginScreen} />
<Stack.Screen name="Home" component={HomeScreen} options={{headerRight: () => (
<Button
onPress={handleSignOut}
title="Sign Out"
color="#0782F9" /> ),}} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="Forget Password" component={ForgetPasswordScreen} />
<Stack.Screen name="SubScreen1" component={SubScreen1} options={{ title: 'Search Result' }}/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
The button seems to appear but I cannot use it at all. It seems this line import { auth } from '../firebase'; is giving me issue. But I don't get it why. I used the same exact line in my home screen, login screen and register screen and it all worked.
import { auth } from '../firebase'; gives me the error of Unable to resolve "../firebase" from "App.js"
I searched online and tried import { auth } from 'firebase/app' gives me the error of {"error": [TypeError: undefined is not an object (evaluating 'util.getModularInstance(auth).signOut')]}
My directory to firebase: Projects/FYPAPP/node_modules/firebase
Under the firebase.js
I keep getting the error when I write this:
const Stack = createStackNavigator();
i have installed everything required for it but it gives me this error
undefined is not an object (evaluating 'Object.keys(routeConfigs)')
Here is my code:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {Home} from './screens/homepage/home';
import { useFonts } from 'expo-font';
import { NavigationContainer, StackActions } from '#react-
navigation/native';
import { createStackNavigator } from 'react-navigation-stack';
export default function App() {
function HomeScreen() {
return(
<View>
<Text>Hello</Text>
</View>
)
}
const Stack = createStackNavigator();
let [fontsLoaded] = useFonts({
'Main': require('./assets/century.ttf'),
'Main-Bold': require('./assets/century-bold.ttf')
});
if(!fontsLoaded) {
return <Text>Waiting...........</Text>
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen style={styles.container} name="Home" component=
{HomeScreen}/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
height:'100%',
backgroundColor:'#141F2B',
},
});
try installing #react-navigation/stack and #react-navigation/native, then try doing this:
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
const Stack = createStackNavigator();
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
style= {styles.container}
name= "Home"
component= {HomeScreen}
/>
</Stack.Navigator>
</NavigationContainer>
I simply want my render method code run after update state in useEffect.
I store my token in asyncStorage and now I want if token exist then it redirect at homePage else return at loginPage. But problem is that render method run before updating my state in useEffect. And it keeps me on loginPage even I had token.
My code Is below:
import React, { useEffect, useState } from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { StyleSheet, Text, View} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { useLinkProps, useNavigation, CommonActions } from '#react-navigation/native';
import MainScreen from './src/screens/MainScreen';
import SignIn from './src/screens/SignIn';
const Stack = createStackNavigator();
const App = props => {
const [isSigned, setIsSigned] = useState(false);
useEffect(async () => {
let token = await AsyncStorage.getItem("#token");
console.log("token", token);
if (token) {
setIsSigned(true);
props.navigation.navigate('MainScreen');
}
else {
setIsSigned(false);
props.navigation.navigate('SignIn');
}
}, [])
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={isSigned ? "MainScreen" : "SignIn"}
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="Login" component={SignIn} />
<Stack.Screen name="MainScreen" component={MainScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const styles = StyleSheet.create({
container: {
marginTop: Platform.OS === 'android' ? '3%' : '10%'
}
});
export default App;
You could add another state value to conditionally render your markup:
import React, { useEffect, useState } from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { StyleSheet, Text, View} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { useLinkProps, useNavigation, CommonActions } from '#react-navigation/native';
import MainScreen from './src/screens/MainScreen';
import SignIn from './src/screens/SignIn';
const Stack = createStackNavigator();
const App = props => {
// Manage state whether component has been loaded.
const [isLoaded, setIsLoaded] = useState(false);
const [isSigned, setIsSigned] = useState(false);
useEffect(() => {
let token = await AsyncStorage.getItem("#token");
if (token) {
setIsSigned(true);
props.navigation.navigate('MainScreen');
} else {
setIsSigned(false);
props.navigation.navigate('SignIn');
}
// Tell your component it's been loaded.
setIsLoaded(true);
}, []);
// Don't render if not loaded.
if (!isLoaded) return <></>;
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={isSigned ? "MainScreen" : "SignIn"}
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="Login" component={SignIn} />
<Stack.Screen name="MainScreen" component={MainScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
I am trying to do user authentication using Firebase + Expo.
Currently working on the Authentication flows, but I keep getting this error that i can't seem to fix :(
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {login} from './login';
import {pageOne} from './pageOne';
import {pageTwo} from './pageTwo';
import 'react-native-gesture-handler';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import {firebase} from './fbConfig';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
return (
firebase.auth().onAuthStateChanged((authenticate)=>{
if (authenticate) {
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="PageOne"
component={pageOne}
/>
<Tab.Screen
name="PageTwo"
component={pageTwo}
/>
</Tab.Navigator>
</NavigationContainer>
} else
{
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={login}
/>
</Stack.Navigator>
</NavigationContainer>
}
})
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Its currently showing an error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
It is because the React is expecting a component to be returned from the App function but here you are returning the firebase.auth().onAuthStateChanged() which ultimately does not return any component. It(onAuthStateChanged) helps to register a listener for user's auth state change and returns a method to unsubscribe from the firebase auth listener on the component unmount(or whenever you would like to remove the auth state listener).
You can check this link for more details.
In your example, you can do it something like:
import React, {useState} from 'react';
import { StyleSheet} from 'react-native';
import { login } from './login';
import { pageOne } from './pageOne';
import { pageTwo } from './pageTwo';
import 'react-native-gesture-handler';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { firebase } from './fbConfig';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
function onAuthStateChanged(user) {
setUser(user);
if (initializing) setInitializing(false);
}
useEffect(() => {
const subscriber = firebase.auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
if (initializing) return null;
if (!user) {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={login}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="PageOne"
component={pageOne}
/>
<Tab.Screen
name="PageTwo"
component={pageTwo}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
I am making a pokedex, and from the FlatList I would like users to be able to click on a pokemon, and get directed to a detail page of the particular pokemon. However, the navigation.navigate is not working in both class and function based views. I have looked at the documentation and it uses the method I am trying to implement. What is going wrong?
App.js
import * as React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import PokedexData from './components/PokedexData';
import PokemonView from './components/PokedexData';
const Stack = createStackNavigator();
export default class App extends React.Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
options={{headerShown: false}}
name="PokedexList"
component={PokedexData}
/>
<Stack.Screen name="PokemonView" component={PokemonView} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
PokedexData.js
import React, {Component} from 'react';
import axios from 'axios';
import {View, FlatList} from 'react-native';
import PodexRow from './PokedexRow';
export default class PokedexRow extends Component {
constructor(props) {
super(props);
this.state = {
pokemon: [],
};
}
componentDidMount() {
this.loadPokemon();
}
loadPokemon = () => {
axios
.get('https://pokeapi.co/api/v2/pokemon?limit=151')
.then((res) => this.setState({pokemon: res.data.results}));
};
renderItem = ({item}) => <PodexRow name={item.name} />;
render() {
return (
<FlatList
data={this.state.pokemon}
renderItem={this.renderItem}
keyExtractor={(item) => item.url}
/>
);
}
}
PokedexRow.js
import {View, StyleSheet, Text, Pressable} from 'react-native';
export default class PokedexRow extends Component {
constructor(props) {
super(props);
}
onPressHandle = () => {
alert(this.props.name);
const {navigate} = this.props.navigation;
};
render() {
return (
<View style={styles.PokedexRowView}>
<Pressable
style={styles.PokedexClickable}
onPress={this.onPressHandle}
android_ripple={{color: 'gray'}}>
<Text style={styles.PokedexRowText}>{this.props.name}</Text>
</Pressable>
</View>
);
}
}
const styles = StyleSheet.create({
/*stles removed for brevity*/
});
Because PokedexRow is not a screen where you get the navigation prop automatically. you can use useNavigation for a functional component to get access to navigation object or pass navigation from screen component to your PokedexRow as a prop.
renderItem = ({item}) => <PodexRow navigation={this.props.navigation} name={item.name} />;