Detect internet connection in react native anywhere in screens - javascript

I'm confused on how can I show my customDialog whenever I don't have Internet connection to my app, Currently, I've only managed to show my customDialog within LoginScreen , I just want to show my custom dialog from different screen not only on LoginScreen whenever I don't have Internet connection, what is the best way to implement it ? should I put CustomDialog each of the screen?
Someone have the idea to implement this? need help
screens/LoginScreen
import NetInfo from "#react-native-community/netinfo";
import CustomAlert from '../components/CustomAlert';
const LoginScreen = ({navigation}) => {
const [modalVisible, setModalVisible] = useState(false);
const [internetCon, setNet] = useState(true);
React.useEffect(()=>{
checkInternetConnection();
}, [])
function checkInternetConnection() {
NetInfo.addEventListener(state =>{
if (state.isConnected==false){
setModalVisible(true);
setNet(false);
console.log("Connection types", state.type);
console.log("Your device appears to have no internet connectivity. Please check your connection settings and try again");
}
else{
console.log("Connected to internet?", state.isConnected);
}
});
}
return (
<ScrollView style={styles.container} >
{internetCon ===false ? //Check if Internet Existt
<CustomAlert
modalVisible={modalVisible}
setModalVisible={setModalVisible}
title={'Message'}
message={'Your device appears to have no internet connectivity. Please check your connection settings and try again'}
buttons={[{
text: 'Retry',
func: () => {checkInternetConnection();}
}]}
/>
:null}
</ScrollView>
);
};
export default LoginScreen;
navigation/AppStack.js
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const FeedStack = ({navigation}) => (
<Stack.Navigator
screenOptions={{
headerShown: false
}}>
<Stack.Screen
name="test"
component={HomeScreen}
options={{
headerTitleAlign: 'center',
headerTitleStyle: {
color: '#2e64e5',
fontFamily: 'Kufam-SemiBoldItalic',
fontSize: 18,
},
headerStyle: {
shadowColor: '#fff',
elevation: 0,
},
headerRight: () => (
<View style={{marginRight: 10}}>
<FontAwesome5.Button
name="plus"
size={22}
backgroundColor="#fff"
color="#2e64e5"
onPress={() => navigation.navigate('AddPost')}
/>
</View>
),
}}
/>
<Stack.Screen
name="HomeProfile"
component={ProfileScreen}
options={{
title: '',
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: '#fff',
shadowColor: '#fff',
elevation: 0,
},
headerBackTitleVisible: false,
headerBackImage: () => (
<View style={{marginLeft: 15}}>
<Ionicons name="arrow-back" size={25} color="#2e64e5" />
</View>
),
}}
/>
</Stack.Navigator>
);
const AppStack = () => {
const getTabBarVisibility = (route) => {
const routeName = route.state
? route.state.routes[route.state.index].name
: '';
if (routeName === 'Chat') {
return false;
}
return true;
};
return (
<Tab.Navigator
screenOptions={{
headerShown: false
}}
tabBarOptions={{
activeTintColor: '#2e64e5',
}}>
<Tab.Screen
name="Home"
component={FeedStack}
options={({route}) => ({
tabBarLabel: 'Home',
// tabBarVisible: route.state && route.state.index === 0,
tabBarIcon: ({color, size}) => (
<MaterialCommunityIcons
name="home-outline"
color={color}
size={size}
/>
),
})}
/>
<Tab.Screen
name="Messages"
component={MessageStack}
options={({route}) => ({
tabBarVisible: getTabBarVisibility(route),
tabBarIcon: ({color, size}) => (
<Ionicons
name="chatbox-ellipses-outline"
color={color}
size={size}
/>
),
})}
/>
</Tab.Navigator>
);
};
export default AppStack;
navigation/AuthProvider.js
export const AuthContext = createContext();
export const AuthProvider = ({children}) => {
const [user, setUser] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
return (
<AuthContext.Provider
value={{
user,
setUser,
googleLogin: async () => {
try {
// Get the users ID token
const Token = await GoogleSignin.signIn();
const {idToken} = Token;
console.log("Processing");
console.log("ok");
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
await auth().signInWithCredential(googleCredential)
.catch(error => {
console.log('Something went wrong with sign up: ', error);
});
} catch(error) {
// console.log('Something went wrong with sign up: ', error);
}
},
}}>
{children}
</AuthContext.Provider>
);
};

Yo can try react-navigation modal: https://reactnavigation.org/docs/modal/

Related

Need help using goBack() in react native

I am having trouble with the back button in the stack navigator. I am getting an error saying undefined is not an object (evaluating '_this.props'). if it works it goes back to a different tab in the bottom stack navigator. Like if I click on book a session and then I go to home and click back it will take me to book a session no matter what. Please help.
This is what I have so far:
function HomeScreen({ navigation }) {
return (
<WebView
source={{
uri: 'https://www.stoodnt.com/'
}}
style={{ marginTop: -120 }}
/>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home"
component={HomeScreen}
options={{
headerLeft: () => (
<HeaderBackButton
onPress={() => this.props.navigation.goBack(null)}
/>
),
}}
/>
</HomeStack.Navigator>
);
}
Bottom Navigator:
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-home'
: 'ios-home';
} else if (route.name === 'Book Session') {
iconName = focused ? 'ios-calendar' : 'ios-calendar';
}
else if (route.name === 'Blogs') {
iconName = focused ? 'ios-bookmark' : 'ios-bookmark';
}
else if (route.name === 'Online Courses') {
iconName = focused ? 'ios-book' : 'ios-book';
}
else if (route.name === 'Classes') {
iconName = focused ? 'ios-desktop' : 'ios-desktop';
}
return <Ionicons name={iconName} size={40} color={'orange'} />;
},
})}
tabBarOptions={{
activeTintColor: '#000000',
inactiveTintColor: '#616161',
labelStyle: {
fontSize: 10,
},
style: {
backgroundColor: '#F7F7F7',
},
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Book Session" component={BookStackScreen} />
<Tab.Screen name="Blogs" component={BlogStackScreen} />
<Tab.Screen name="Online Courses" component={OnlineStackScreen} />
<Tab.Screen name="Classes" component={ClassesStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
try the below code :
function HomeScreen(props) {
return (
<WebView
source={{
uri: 'https://www.stoodnt.com/'
}}
style={{ marginTop: -120 }}
/>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home"
component={HomeScreen}
options={({ route, navigation }) => ({
headerLeft: () => (
<HeaderBackButton
onPress={() => navigation.goBack(null)}
/>
),
})}
/>
</HomeStack.Navigator>
);
}```
In functional components, you do not have access to "this". You should either pass navigation from the screen options or use useNavigation.
<HomeStack.Screen
...
options={({navigation}) => (
headerLeft: () => (
// you can use navigation.goBack() here
)
)}
or
// this should be added right under function HomeStackScreen,
// right above the return and you will be able to use it in the screens
const navigation = useNavigation()

How to navigate from linking (deep linking with branch.io) when navigator hasn't been created yet?

I pretty much followed both react-navigation deep linking and branch.io react-native documentation, and either both are deprecated or just not completely helpful.
All I want is that whenever a deep link reads from the linking, navigate to a certain screen, I'm not looking to implement a listener on a specific screen, I want this on a root path, and is either the onReady (which for me didn't work) or linking from navigator container
this is my code, very simple
const linking: LinkingOptions = {
prefixes: ['agendameio://', 'https://agendame.io', 'https://agendameio.app.link', 'https://agendameio.app-alternative.link'],
subscribe(listener) {
const navigation = useNavigation();
const onReceiveURL = ({ url }: { url: string }) => listener(url);
Linking.addEventListener('url', onReceiveURL);
branch.skipCachedEvents();
branch.subscribe(async ({ error, params, uri }) => {
if (error) {
console.error('Error from Branch: ' + error);
return;
}
if (params) {
DataManager.DynamicURL = params['~id'] === "951933826563912687" ? params.id : undefined;
}
let url = params?.['+url'] || params?.['~referring_link']; // params !== undefined ? `agendameio://empresa/${params.id}` : 'agendameio://empresa';
navigation.navigate(`DetalleEmpresa${params.id}`);
listener(url);
});
return () => {
Linking.removeEventListener('url', onReceiveURL);
branch.logout();
};
},
I instantly get an error due to use navigation, but I really don't know what else to use to navigate to inside the app
EDIT: this is the error in particular
EDIT 2: I'll add my navigation so it can help to understand my problem
function firstStack() {
return (
<homeStack.Navigator initialRouteName="EmpresasScreen">
<homeStack.Screen
options={({navigation}) => ({
headerShown: false,
headerTitle: () => (
<>
<View style={styles.viewHeader}>
<Image
resizeMode="contain"
style={styles.imageLogo}
source={Images.iconoToolbar}
/>
</View>
</>
),
})}
name="EmpresasScreen"
component={EmpresasScreen}
/>
<detalleEmpresaStack.Screen
options={{ headerShown: false }}
name="DetalleEmpresaScreen"
component={DetalleEmpresaScreen}
/>
<agendamientoStack.Screen
options={{ headerShown: false }}
name="AgendamientoScreen"
component={AgendamientoScreen}
/>
</homeStack.Navigator>
);
}
function secondStack() {
return (
<misCitasStack.Navigator>
<misCitasStack.Screen
options={({navigation}) => ({
headerShown: false,
headerTitle: () => (
<>
<View style={styles.viewHeader}>
<Image
resizeMode="contain"
style={styles.imageLogo}
source={Images.iconoToolbar}
/>
</View>
</>
),
})}
name="MisCitasScreen"
component={CitasScreen}
/>
<detalleCitasStack.Screen
options={({navigation}) => ({
headerShown: false,
})}
name="DetalleCitaScreen"
component={DetalleCitaScreen}
/>
</misCitasStack.Navigator>
);
}
function tabStack() {
return (
<tab.Navigator
screenOptions={({route}) => ({
tabBarIcon: ({focused}) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? Images.casaActive
: Images.casa
} else if (route.name === 'Citas') {
iconName = focused
? Images.citasActive
: Images.citas
}
return <Image source={iconName} />
}
})}
tabBarOptions={{
showLabel: false,
}}>
<tab.Screen name="Home" component={firstStack} />
<tab.Screen name="Citas" component={secondStack} />
</tab.Navigator>
);
}
function menuStackNavigator() {
useEffect(() => {
VersionCheck.needUpdate({forceUpdate: true}).then(async res => {
if (res.isNeeded) {
alertNeedUpdate(res.storeUrl, false);
}
});
if(Platform.OS === 'android') {
NativeModules.SplashScreenModule.hide();
}
}, [])
return (
<NavigationContainer linking={linking}>
<stack.Navigator headerMode="none">
<stack.Screen name="Home" component={tabStack} />
<stack.Screen name="Error" component={ErrorScreen} />
</stack.Navigator>
</NavigationContainer>
);
};
const styles = StyleSheet.create({
viewHeader: {
alignItems: 'center',
justifyContent: 'center',
},
imageLogo: {
alignItems: 'center',
justifyContent: 'center',
marginTop: 6,
marginBottom: 6
}
});
export default menuStackNavigator;
you can use Configuring links to open the target screen directly.
see more example here configuring-links
Here the URL /feed will open screen named Chat.
import { NavigationContainer } from '#react-navigation/native';
const linking = {
prefixes: ['https://mychat.com', 'mychat://'],
config: {
screens: {
Chat: 'feed/:sort', //URL `/feed` will open screen named `Chat`.
Profile: 'user',
}
},
};
function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<Stack.Navigator>
<Stack.Screen name="Chat" component={ChatScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
or use navigationRef.
read about it navigating-without-navigation-prop.
wait to navigation to be ready and then navigate.
import { createNavigationContainerRef } from '#react-navigation/native';
function App() {
const navigationRef = createNavigationContainerRef();
const navigateWhenNavigationReady = (routeName, params, n = 0) => {
setTimeout(() => {
if (navigationRef?.getRootState()) {
navigationRef.navigate(routeName, params)
}else if (n < 100) {
navigateWhenNavigationReady(routeName, params, n + 1);
}
}, 300)
}
const linking = {
...,
subscribe(listener) {
...
navigateWhenNavigationReady("Chat", {id: 123});
}
};
return (
<NavigationContainer ref={navigationRef} linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Chat" component={ChatScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
can you try doing it like this
const App = () => {
return (
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
);
};
and do the subscribe in
export const AppNavigator = () => {
const navigation =useNavigation();
useEffect(()=>{
//add listener and navigation logic here
},[]);
return (
<Stack.Navigator >
...
</Stack.Navigator>
);
};
this will make navigation context to be available in AppNavigator Component
Answer use custom hook useNavigationWhenReady.
const useNavigationWhenReady = (isReady, navigationRef) => {
const [routeName, setRouteName] = React.useState();
const [routeParams, setRouteParams] = React.useState({});
const [navigationAction, setNavigationAction] = React.useState("navigate");
React.useEffect(() => {
if (isReady && routeName) {
if(navigationRef && navigationRef[navigationAction]) {
const _navigationAction = navigationRef[navigationAction];
_navigationAction(routeName, routeParams);
}
}
}, [isReady, routeParams, routeParams]);
const navigate = (_routeName, _routeParams = {}) => {
if(!routeName) {
setNavigationAction("navigate");
setRouteParams(_routeParams);
setRouteName(_routeName);
}
};
const reset = (state) => {
if(!routeName) {
setNavigationAction("reset");
setRouteName(state);
}
};
return { navigate, reset }
};
you can now use useNavigationWhenReady instead of useNavigation;
import { createNavigationContainerRef } from '#react-navigation/native';
function App() {
const [isReady, setReady] = React.useState(false);
const navigationRef = createNavigationContainerRef();
//define it here
const navigation = useNavigationWhenReady(isReady, navigationRef);
const handleOpenNotificationOrOpenLinking = () => {
...
navigation.navigate("screenName", {param1: value1});
//or use reset
//navigation.reset({
//index: 1,
//routes: [{ name: 'screenName' }]
//});
};
return (
<NavigationContainer ref={navigationRef} onReady={() => setReady(true)}>
</NavigationContainer>
);
}
if you use react-navigation-v5 not v6 use
//const navigationRef = createNavigationContainerRef();
//const navigation = useNavigationWhenReady(isReady, navigationRef);
const navigationRef = React.useRef();
const navigation = useNavigationWhenReady(isReady, navigationRef?.current);
you can also show loading or splash screen while navigation not ready
return (
<NavigationContainer ref={navigationRef} onReady={() => setReady(true)}>
<RootStack.Navigator initialRouteName={isReady ? "home" : "loading"} >
</RootStack>
</NavigationContainer>
);

React Native tab navigator nested in stack navigator

I'm relatively new to React Native and am struggling with screens. I've gone through the nested navigation documentation (https://reactnavigation.org/docs/nesting-navigators/) which helped me setup the initial nav setup i have, but im having some issues.
I'm attempting to setup the app so it has the initial screen as a "Select User" which has no tab navigation. After selecting the user, you are redirected to a another screen which has tab navigation. I currently have it working however I am unable to access any route/props/params after the initial screen.
I've had to manually import navigation with import { useNavigation } from "#react-navigation/native"; and even though I'm providing params in the Navigation.push, trying to access {route} in my screens states that route is undefined.
My setup of the screens looks similar to below:
const Tab = createBottomTabNavigator();
const WelcomeStack = createStackNavigator();
const HomeStack = createStackNavigator();
const SettingsStack = createStackNavigator();
const Stack = createStackNavigator();
const WelcomeStackScreen = () => (
<WelcomeStack.Navigator>
<WelcomeStack.Screen
name="Welcome"
component={WelcomeScreen}
options={{ headerShown: false }}
/>
<WelcomeStack.Screen
name="SignUp"
component={SignUpScreen}
options={{ headerShown: false }}
/>
</WelcomeStack.Navigator>
);
const HomeStackScreen = () => {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen
name="Search"
component={SearchResultScreen}
options={({ navigation }) => ({
headerLeft: () => (
<Ionicons
name={"arrow-back"}
size={30}
color={colours.text}
style={{ paddingLeft: 15 }}
onPress={() => {
navigation.goBack();
}}
/>
),
})}
/>
<HomeStack.Screen
name="Select"
component={SelectScreen}
options={({ navigation, route }) => ({
title: route.params.name,
headerLeft: () => (
<Ionicons
name={"arrow-back"}
size={30}
color={colours.text}
style={{ paddingLeft: 15 }}
onPress={() => {
navigation.goBack();
}}
/>
),
})}
/>
<HomeStack.Screen name="Read" component={ReaderScreen} />
</HomeStack.Navigator>
);
};
function SettingsScreen() {
return (
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<Text>TBD</Text>
</View>
);
}
const SettingsStackScreen = () => (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
</SettingsStack.Navigator>
);
const TabNavigator = ({ route }) => (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
switch (route.name) {
case "Home":
iconName = focused ? "home" : "home-outline";
break;
case "Settings":
iconName = focused ? "list" : "list-outline";
break;
default:
break;
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: "tomato",
inactiveTintColor: "gray",
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsScreen}/>
</Tab.Navigator>
);
export default function App() {
return (
<NavigationContainer theme={navThemeOverride}>
<Stack.Navigator>
<Stack.Screen name="Welcome" component={WelcomeStackScreen} />
<Stack.Screen
name="TabNavigator"
component={TabNavigator}
options={{ headerShown: false }}
navigationOptions={{ gesturesEnabled: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Below is a snippet from the Welcome screen navigation
navigation.push("TabNavigator", {
screen: "Home",
params: {
userId: userId,
},
});
Your Home screen you're trying to navigate to from your parent-tab-navigator ... is also a StackNavigator ... and you wanna navigate to Select screen I guess ... so there's an extra level needed for your navigation to work...
navigation.navigate('TabNavigator', {
screen: 'Home', // <--- StackNavigator
params: {
screen: 'Select', // <-- nested inside HomeStack
params: {
title: 'Your custom title for Select screen here ...',
},
},
});
Plus +
There's a double definition for route in your Tab navigator
const TabNavigator = ({ route }) => ( //<--- here
<Tab.Navigator
screenOptions={({ route }) => ({ // <- and here
Instead
function TabNavigator() {
return <Tab.Navigator screenOptions={({ route }) => ({})>{/* ...Tabs... */}</Tab.Navigator>;
}

Setting params on useEffect and getting the params it on createStackNav in React Navigation 5

I am working on an app where in I need to send or set a particular params of my state in an object.
Here's my object with its respected state:
const [isGlutenFree, setIsGlutenFree] = useState(false);
const [isLactoseFree, setisLactoseFree] = useState(false);
const [isVegan, setIsVegan] = useState(false);
const [isVegetarian, setIsVegetarian] = useState(false);
const saveFilters = () => {
const appliedFilters = {
glutenFree: isGlutenFree,
lactosFree: isLactoseFree,
vegan: isVegan,
isVegetarian: isVegetarian
};
};
These are all sitting on my FiltersScreen.js file. So on my useEffect I tried to use the CommonActions to set the params:
import { CommonActions } from '#react-navigation/native';
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ save: saveFilters }));
});
Then inside my Navigator.js file, I tried to receive this and console.log this on the screen to see that it has the data I need:
<FilterNav.Screen
name="Filters"
component={FiltersScreen}
options={({ route }) => ({
title: 'Filters',
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Menu'
iconName='ios-menu'
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Save'
iconName='ios-save'
onPress={() => {
// CONSOLE LOG THE PARAMS SET >>
console.log(route.params.save);
}}
/>
</HeaderButtons>
)
})}
/>
</FilterNav.Navigator>
To be specific on this part:
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Save'
iconName='ios-save'
onPress={() => {
// CONSOLE LOG THE PARAMS SET >>
console.log(route.params.save);
}}
/>
</HeaderButtons>
)
But this doesn't work and doesn't return anything. Also it freezes all of my Switches.
Here's the complete code for FiltersScreen.js:
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Switch, Platform } from 'react-native';
import { CommonActions } from '#react-navigation/native';
import Colors from '../constants/colors';
const FiltersSwitch = props => {
return(
<View style={styles.filterContainer}>
<Text>{ props.label }</Text>
<Switch
trackColor={{ true: Colors.primaryColor }}
thumbColor={Platform.OS === 'android' ? Colors.primaryColor : ''}
value={props.state}
onValueChange={props.onChange} />
</View>
);
};
const FiltersScreen = props => {
const [isGlutenFree, setIsGlutenFree] = useState(false);
const [isLactoseFree, setisLactoseFree] = useState(false);
const [isVegan, setIsVegan] = useState(false);
const [isVegetarian, setIsVegetarian] = useState(false);
// We will give this a connection with the
// MealsNavigator in order to trigger this
// function via useEffect
const saveFilters = () => {
const appliedFilters = {
glutenFree: isGlutenFree,
lactosFree: isLactoseFree,
vegan: isVegan,
isVegetarian: isVegetarian
};
};
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ save: saveFilters }));
});
return (
<View style={styles.screen}>
<Text style={styles.title}>Available Filters / Restrictions</Text>
<FiltersSwitch
label="Glutten Free"
state={isGlutenFree}
onChange={newValue => setIsGlutenFree(newValue)} />
<FiltersSwitch
label="Lactose Free"
state={isLactoseFree}
onChange={newValue => setisLactoseFree(newValue)} />
<FiltersSwitch
label="Vegan"
state={isVegan}
onChange={newValue => setIsVegan(newValue)} />
<FiltersSwitch
label="Vegetarian"
state={isVegetarian}
onChange={newValue => setIsVegetarian(newValue)} />
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
alignItems: 'center'
},
title: {
fontFamily: 'poppins-bold',
fontSize: 22,
margin: 20,
textAlign: 'center'
},
filterContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
width: '80%',
marginVertical: 15
}
});
export default FiltersScreen;
And here's the complete code for the navigation.js file:
const FilterNav = createStackNavigator();
const FiltersNavigator = () => {
return (
<FilterNav.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}
>
<FilterNav.Screen
name="Filters"
component={FiltersScreen}
options={({ route }) => ({
title: 'Filters',
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Menu'
iconName='ios-menu'
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Save'
iconName='ios-save'
onPress={() => {
console.log(route.params.save);
}}
/>
</HeaderButtons>
)
})}
/>
</FilterNav.Navigator>
);
};
Please help how to solve this been stuck for a couple of time. I am using React Navigation 5.
I guess the "freeze" problem is that your useEffect is executing in every screen render, since it has no dependency array (read Tip: Optimizing Performance by Skipping Effects).
Your code should look something like this:
// useCallback, so it doesn't create a new saveFilters function in every render
const saveFilters = useCallback(() => {
const appliedFilters = {
glutenFree: isGlutenFree,
lactosFree: isLactoseFree,
vegan: isVegan,
isVegetarian: isVegetarian
};
}, [isGlutenFree, isLactoseFree, isVegan, isVegetarian]); // This function depends on those variables.
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ save: saveFilters }));
}, [saveFilters]); // It should change the parameterized function if it changes

The action Navigate with payload : SignIn was not handled by any navigator

Am making an authentification flow using react native i have 3 screens Welcome Screen with two buttons to either sign in or sign up , i used react navigation version 5
every time i press either the sign in or sign up button i get the following error " The action Navigate with payload : SignIn was not handled by any navigator " and am not exactly sure why ?
my question is how do i resolve this issue and navigate correctly to the other screen
The code used for the app.js
export default function App() {
const [state, dispatch] = React.useReducer(authReducer, {
token: null,
errorMessage: "",
});
React.useEffect(() => {
const bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem("userToken");
dispatch({ type: "RESTORE_TOKEN", token: userToken });
};
bootstrapAsync();
}, []);
const authContext = React.useMemo(
() => ({
// CLEARING ERROR MESSAGES WHEN SWITCHING SIGNIN-SIGNUP
clearErrorMessage: async () => {
dispatch({ type: "clear_error_message" });
},
// AUTOMATIC SIGNIN ONLY USING TOKENS ON USER DEVICE
tryLocalSignin: async () => {
const navigation = useNavigation();
const token = await AsyncStorage.getItem("token");
if (token) {
// if token exists
dispatch({ type: "SIGN_IN", payload: token });
navigation.navigate("MainTabScreen");
} else {
// if token doesnt exist
navigation.navigate("Welcome");
}
},
signIn: async ({ email, password }) => {
const navigation = useNavigation();
try {
const response = await userAPI.post("/signin", { email, password });
await AsyncStorage.setItem("token", response.data.token);
// using signin since the logic is the same
dispatch({ type: "SIGN_IN", token: response.data.token });
// making use of the navigate component to access navigation
// and redirect the user
navigation.navigate("MainTabScreen");
} catch (err) {
console.log(err);
dispatch({
type: "add_error",
payload: "Something went wrong with sign in",
});
}
},
signOut: async () => {
const navigation = useNavigation();
await AsyncStorage.removeItem("token");
dispatch({ type: "SIGN_OUT" });
navigation.navigate("Welcome");
},
signUp: async ({ email, password }) => {
const navigation = useNavigation();
try {
const response = await userAPI.post("/signup", { email, password });
await AsyncStorage.setItem("token", response.data.token);
dispatch({ type: "SIGN_IN", payload: response.data.token });
// making use of the navigate component to access navigation
// and redirect the user
navigation.navigate("MainTabScreen");
} catch (err) {
dispatch({
type: "add_error",
payload: "Something went wrong with sign up",
});
}
},
}),
[]
);
return (
<AuthContext.Provider value={authContext}>
<NavigationContainer>
{state.token === null ? <AuthNavigator /> : <AppNavigator />}
</NavigationContainer>
</AuthContext.Provider>
);
}
The code used for signinScreen.js
const SigninScreen = ({ navigation }) => {
const { signIn } = React.useContext(AuthContext);
return (
<ImageBackground
source={require("../../assets/background.png")}
style={styles.image}
>
{/* <NavigationEvents onWillBlur={clearErrorMessage} /> */}
<AuthForm
headerText="Welcome back!"
subText="Log in with your email and discover the universe."
//errorMessage={state.errorMessage}
AppOnSubmit={signIn}
submitButtonText="Log in"
/>
</ImageBackground>
);
};
SigninScreen.navigationOptions = () => {
return {
headerShown: false,
};
};
const styles = StyleSheet.create({
container: {
//flex: 1,
//justifyContent: "center",
//marginBottom: 150,
},
image: {
position: "absolute",
left: 0,
top: 0,
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
},
});
export default SigninScreen;
AppNavigator.js :
const AppNavigator = () => (
<AppTabs.Navigator
initialRouteName="MainTabScreen"
activeColor={colors.shade1}
style={{ backgroundColor: "tomato" }}
>
<AppTabs.Screen
name="Home"
component={MainTabScreen}
options={{
tabBarColor: "#292B34",
tabBarIcon: ({ color }) => (
<SimpleLineIcons name="home" size={24} color={colors.shade2} />
),
}}
/>
<AppTabs.Screen
name="SearchScreen"
component={SearchScreen}
options={{
tabBarLabel: "Search",
tabBarColor: "#292B34",
tabBarIcon: ({ color }) => (
<Feather name="search" size={24} color={colors.shade2} />
),
}}
/>
<AppTabs.Screen
name="SaveScreen"
component={SaveScreen}
options={{
tabBarLabel: "Save",
tabBarColor: "#292B34",
tabBarIcon: ({ color }) => (
<Feather name="bookmark" size={24} color={colors.shade2} />
),
}}
/>
<AppTabs.Screen
name="AccountScreen"
component={AccountScreen}
options={{
tabBarLabel: "Account",
tabBarColor: "#292B34",
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
name="account-circle-outline"
size={24}
color={colors.shade2}
/>
),
}}
/>
</AppTabs.Navigator>
);
export default AppNavigator;
AuthNavigator.js
const Stack = createStackNavigator();
const AuthNavigator = () => (
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: "#221e4f" },
headerTintColor: "white",
title: "",
}}
>
<Stack.Screen
name="Welcome"
component={WelcomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen name="signIn" component={SigninScreen} />
<Stack.Screen name="signup" component={SignupScreen} />
</Stack.Navigator>
);
This approach that you are doing is error prone. I believe, you shouldn't use navigation.navigate('SomeScreen') on your context functions.
For example; SIGN_IN action is dispatched and it will change the state of token which you are using as conditional to decide which navigator should it use. Dispatch function is not an async so you can't rely on React to change state and mount AppNavigator before navigation.navigate("MainTabScreen") is called. Instead use initialRouteName="MainTabScreen" prop.
I don't know anything about your AppNavigator or AuthNavigator but; misusing of this approach can cause the root of the problem. If you share more detail about your navigators we can help more.

Categories

Resources