How to navigate to nested screen in react-native - javascript

I am trying to navigate to another screen with in my album component when the user click
on a album but I keep getting this error
The action 'NAVIGATE' with payload {"name":"AlbumScreen"} was not handled by any navigator.
below is my code and my try
navigation->Album->index.tsx
import React from 'react';
import { View, Image, Text, TouchableWithoutFeedback } from 'react-native';
import styles from './style';
import { Album, TabOneParamList } from '../../types';
import { useNavigation } from '#react-navigation/native';
// import Navigation from '../navigation';
export type AlbumProps = {
album: Album,
}
const AlbumComponent = (props: AlbumProps) => {
const navigation = useNavigation();
const onPress = () => {
navigation.navigate('AlbumScreen');
}
return (
<TouchableWithoutFeedback onPress={onPress}>
<View style={styles.container}>
<Image source={{ uri: props.album.imageUri }} style={styles.image} />
<Text style={styles.text}>{props.album.artistsHeadline}</Text>
</View>
</TouchableWithoutFeedback>
);
}
export default AlbumComponent;
here is my AlbumScreen.tsx also this is the screen I want to user to move to once they click on the Album
import React from 'react';
import { View, Text } from 'react-native';
const AlbumScreen = () => (
<View>
<Text style={{ color: 'white' }} >Hello from Album Screen</Text>
</View>
);
export default AlbumScreen;
also here is my BottomTabNavigation which I add the newly created AlbumScreen just the way the HomeScreen is added
import {
Ionicons,
Entypo,
EvilIcons,
MaterialCommunityIcons,
FontAwesome5,
FontAwesome
} from '#expo/vector-icons';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createStackNavigator } from '#react-navigation/stack';
import * as React from 'react';
import Colors from '../constants/Colors';
import useColorScheme from '../hooks/useColorScheme';
import TabOneScreen from '../screens/HomeScreen';
import AlbumScreen from '../screens/AlbumScreen';
import TabTwoScreen from '../screens/TabTwoScreen';
import { BottomTabParamList, TabOneParamList, TabTwoParamList } from '../types';
const BottomTab = createBottomTabNavigator<BottomTabParamList>();
export default function BottomTabNavigator() {
const colorScheme = useColorScheme();
return (
<BottomTab.Navigator
initialRouteName="Home"
tabBarOptions={{ activeTintColor: Colors[colorScheme].tint }}>
<BottomTab.Screen
name="Home"
component={TabOneNavigator}
options={{
tabBarIcon: ({ color }) => <Entypo name="home" size={30} style={{ marginBottom: -3 }} color={color} />,
}}
/>
<BottomTab.Screen
name="store"
component={TabTwoNavigator}
options={{
tabBarIcon: ({ color }) => <MaterialCommunityIcons name="store" size={30} style={{ marginBottom: -3 }} color={color} />,
}}
/>
<BottomTab.Screen
name="Library"
component={TabTwoNavigator}
options={{
tabBarIcon: ({ color }) => <Ionicons name="library-outline" size={30} style={{ marginBottom: -3 }} color={color} />,
}}
/>
<BottomTab.Screen
name="Profile"
component={TabTwoNavigator}
options={{
tabBarIcon: ({ color }) => <FontAwesome name="user-circle" size={28} style={{ marginBottom: -3 }} color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
// Each tab has its own navigation stack, you can read more about this pattern here:
// https://reactnavigation.org/docs/tab-based-navigation#a-stack-navigator-for-each-tab
const TabOneStack = createStackNavigator<TabOneParamList>();
function TabOneNavigator() {
return (
<TabOneStack.Navigator>
<TabOneStack.Screen
name="TabOneScreen"
component={TabOneScreen}
options={{ headerTitle: 'Home' }}
/>
<TabOneStack.Screen
name="AlbumScreen"
component={AlbumScreen}
options={{ headerTitle: 'Album' }}
/>
</TabOneStack.Navigator>
);
}
const TabTwoStack = createStackNavigator<TabTwoParamList>();
function TabTwoNavigator() {
return (
<TabTwoStack.Navigator>
<TabTwoStack.Screen
name="TabTwoScreen"
component={TabTwoScreen}
options={{ headerTitle: 'Tab Two Title' }}
/>
</TabTwoStack.Navigator>
);
}
Here is the picture of what I am trying to do I want upon clicking on the album on the right of this image the user is direct to another screen but I keep getting the Highlighted error my errors

You will need to specify which stack you are targeting when you attempt to navigate.
https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator

Related

My tab navigation header button doesnt working. How can I fix it?

I'm trying to learn react native and I have a problem. Here I use tab navigation and I want to add an image in my headers right side and when the users touch that image I want use navigator, but it doesn't work.
import React, { useEffect, useState } from 'react'
import { View, StyleSheet, Image, TouchableOpacity } from 'react-native'
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs'
import { createStackNavigator } from '#react-navigation/stack';
const TabNavigation = ({ navigation }) => {
return (
enter code here
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: () => (<Image source={require('../Images/Floody.png')} />),
headerStyle: {
height: 100
},
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity>
<Image source={require('../Images/profile.jpg')} />
</TouchableOpacity>
),
tabBarIcon: ({ focused }) => focused ? <HomeImageColorfull /> : <HomeImageBlack />
}} />
The navigation object is passed by the navigation framework to the options of the screen. Hence, you can implement a onPress function for your TouchableOpacity of your headerRight component and use the navigation object from the options.
<Tab.Screen
name="Home"
component={HomeScreen}
options={({navigation}) => ({
headerTitle: () => (<Image source={require('../Images/Floody.png')} />),
headerStyle: {
height: 100
},
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity onPress={() => navigation.navigate("SomeScreenToNavigateTo")}>
<Image source={require('../Images/profile.jpg')} />
</TouchableOpacity>
),
tabBarIcon: ({ focused }) => focused ? <HomeImageColorfull /> : <HomeImageBlack />
})} />

Lifting a state so I can modify it with an onPress

I am trying to figure out how lifting states work. Currently, I am trying to use onPress in a different component to change the state. In the snack demo I provided below, ListHome and MapHome do not respond for some reason, but you will still see what I am trying to accomplish.
I want the map and list "buttons" to accomplish what the "click for this" does.
Demo - No errors with the current implementation, just no response other than the flash from touchable opacity. (Also remember the snack does not respond for some reason. My local device works fine)
EDIT: So to be clear, I want to be able to access and change the state whichComponentToShow from another component. i.e the ListHome Component.
export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: true,
whichComponentToShow: 'Screen1',
};
}
goToMap = () => {
this.setState({ whichComponentToShow: 'Screen2' });
};
render(){
if(this.state.whichComponentToShow === 'Screen1'){
return(
<View style={{backgroundColor: '#d1cfcf' ,flex: 1}}>
<ListHome
renderMap = {this.goToMap.bind(this)}
/>
}
}
export default class ListHome extends React.Component {
goToMap = () => {
this.props.renderMap();
}
<TouchableOpacity onPress={() => this.goToMap.bind(this)}>
<View style={styles.conditionalMap}>
<View style={{justifyContent: 'center', alignItems: 'center'}}>
<Text style={{color: 'black', fontSize: scale(15)}}>
Map
</Text>
</View>
</View>
</TouchableOpacity>
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import VenueDetailsScreen from './screens/VenueDetails';
import CarouselGallary from './screens/Carousel';
import Home from './screens/Home';
import Friends from './screens/Friends';
import Profile from './screens/Profile';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Stack.Screen
name="VenueDetails"
component={VenueDetailsScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="CarouselGallary"
component={CarouselGallary}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Friends"
component={Friends}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Profile"
component={Profile}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#F60081',
tabBarInactiveTintColor: '#4d4d4d',
tabBarStyle: {
backgroundColor: '#d1cfcf',
borderTopColor: 'transparent',
},
}}>
<Tab.Screen
name="Home"
component={MyTabs}
options={{
tabBarLabel: 'Home',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Friends"
component={Friends}
options={{
tabBarLabel: 'Friends',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons
name="account-group"
color={color}
size={size}
/>
),
}}
/>
<Tab.Screen
name="Profile"
component={Profile}
options={{
tabBarLabel: 'Profile',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons
name="account"
color={color}
size={size}
/>
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
What you are trying to achieve is a common use case, which is to update the state in an ancestor component. You essentially have a component tree that looks like this:
You are trying to update the state of the Home component from the ListHome component with the renderMap prop that sets the state of the Home screen. This makes sense, so you have basic principle down, the reason why it is not working is due to a minor mistake in the ListHome component
<View style={styles.conditionalMap}>
<TouchableOpacity onPress={() => this.goToMap.bind(this)}>
// In the above line, change to this.goToMap()
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'black', fontSize: scale(15) }}>Map</Text>
</View>
</TouchableOpacity>
</View>
This is what the bind method does:
The bind() method creates a new function that, when called, has this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
So the way onPress is currently defined, it will create a new function, but that function is never actually called. Changing this.goToMap.bind(this) to this.goToMap() should solve your problem.

How to navigate between 2 different navigators after login and logout in react native

I am a bit new to react native and I am having difficulty navigate between 2 different tab navigators after login and logout in react native screens of my app that all use function component screens
Can someone advice me on how to do this.
In my Navigation directory(which handle navigation flow), I have 2 files index.js and main.js
navigation/index.js
navigation/main.js
Once the user launches the app, the user sees screens in the navigation/index.js file.
After the user either signs up or signs in, the users see screens in the navigation/main.js file
I have implemented everything else in the app except navigate between navigators after login and logout in the app
I initially put all screens in the same navigator file but I started having issues after pressing the back button continuously
see the code below
:::::::::EDITS:::::::::::
No longer using Navigation/main.js
Now using only Navigation/index.js and implementing what was suggested by #Tolga Berk Ordanuç
Still need more help though because
It worked partially
Now
After logging in by clicking login I get the error
" ERROR The action 'NAVIGATE' with payload {"name":"BottomTabNavigator"} was not handled by any navigator.
Do you have a screen named 'BottomTabNavigator'?"
And when I close the app completely by hitting back numerous times,
I am logged in
But now second error, when I click logout
I get below error
ERROR The action 'NAVIGATE' with payload {"name":"Walkthrough"} was not handled by any navigator.
Do you have a screen named 'Walkthrough'?
home.js
import React, {useState} from 'react';
import {View} from 'react-native';
import {useTheme} from '#config';
import {Header, Icon, Button} from '#components';
import {useDispatch} from 'react-redux';
export default function Home({navigation, route}) {
const {colors} = useTheme();
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
const logout = () => {
setLoading(true);
dispatch(setIsLoggedIn(false));
navigation.navigate('SignIn');
setLoading(false);
};
return (
<View style={{flex: 1}}>
<Header
title="sign in"
renderLeft={() => {
return (
<Icon
name="times"
size={20}
color={colors.primary}
enableRTL={true}
/>
);
}}
onPressLeft={() => {
navigation.goBack();
}}
/>
<View>
<Button style={{marginTop: 20}} full loading={loading} onPress={logout}>
Log Out
</Button>
</View>
</View>
);
}
Navigation/main.js
import React from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import {useTheme} from '#config';
import {useSelector} from 'react-redux';
import {designSelect} from '#selectors';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {BaseColor, useFont} from '#config';
import {Icon} from '#components';
/* Main Stack Navigator */
/* Modal Screen only affect iOS */
import Savings from 'Savings';
import Loans from 'Loans';
import Home from 'Home';
import Profile from 'Profile';
/* Stack Screen */
import Setting from 'Setting';
import Feedback from 'Feedback';
import ChangePassword from 'ChangePassword';
import ProfileEdit from 'ProfileEdit';
import ContactUs from 'ContactUs';
import AboutUs from 'AboutUs';
import Support from 'Support';
const MainStack = createStackNavigator();
export default function Main() {
return (
<MainStack.Navigator
screenOptions={{
headerShown: false,
}}
initialRouteName="BottomTabNavigator">
<MainStack.Screen
name="BottomTabNavigator"
component={BottomTabNavigator}
/>
<MainStack.Screen name="Support" component={Support} />
<MainStack.Screen name="SavingsTemp" component={Savings} />
<MainStack.Screen
name="BottomTabNavigator"
component={BottomTabNavigator}
/>
<MainStack.Screen name="Setting" component={Setting} />
<MainStack.Screen name="Feedback" component={Feedback} />
<MainStack.Screen name="ChangePassword" component={ChangePassword} />
<MainStack.Screen name="ProfileEdit" component={ProfileEdit} />
<MainStack.Screen name="ContactUs" component={ContactUs} />
<MainStack.Screen name="AboutUs" component={AboutUs} />
</MainStack.Navigator>
);
}
function BottomTabNavigator() {
const {colors} = useTheme();
const font = useFont();
const BottomTab = createBottomTabNavigator();
const design = useSelector(designSelect);
/**
* Main follow return Home Screen design you are selected
* #param {*} design ['basic', 'real_estate','event', 'food']
* #returns
*/
const exportHome = () => {
return Home;
};
return (
<BottomTab.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
}}
tabBarOptions={{
showIcon: true,
showLabel: true,
activeTintColor: colors.primary,
inactiveTintColor: BaseColor.grayColor,
style: {borderTopWidth: 1},
labelStyle: {
fontSize: 12,
fontFamily: font,
paddingBottom: 4,
},
}}>
<BottomTab.Screen
name="Home"
component={exportHome(design)}
options={{
title: 'home',
tabBarIcon: ({color}) => {
return <Icon color={color} name="home" size={20} solid />;
},
}}
/>
<BottomTab.Screen
name="Savings"
component={Savings}
options={{
title: 'Savings',
tabBarIcon: ({color}) => {
return <Icon color={color} name="bookmark" size={20} solid />;
},
}}
/>
<BottomTab.Screen
name="Loans"
component={Loans}
options={{
title: 'Loans',
tabBarIcon: ({color}) => {
return <Icon color={color} name="clipboard-list" size={20} solid />;
},
}}
/>
<BottomTab.Screen
name="Profile"
component={Profile}
options={{
title: 'Profile',
tabBarIcon: ({color}) => {
return <Icon solid color={color} name="user-circle" size={20} />;
},
}}
/>
</BottomTab.Navigator>
);
}
Navigation/index.js
import React, {useEffect, useState} from 'react';
import {StatusBar, Platform} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {DarkModeProvider, useDarkMode} from 'react-native-dark-mode';
import {useTheme, BaseSetting} from '#config';
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import {useSelector} from 'react-redux';
import {languageSelect, designSelect} from '#selectors';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {BaseColor, useFont} from '#config';
import {useTranslation} from 'react-i18next';
import {Icon} from '#components';
/* Main Stack Navigator */
/* Modal Screen only affect iOS */
import Loading from 'Loading';
import SignIn from 'SignIn';
import SignUp from 'SignUp';
import ResetPassword from 'ResetPassword';
import Walkthrough from 'Walkthrough';
import Home from 'Home';
import Profile from 'Profile';
import Feedback from 'Feedback';
import ChangePassword from 'ChangePassword';
import ProfileEdit from 'ProfileEdit';
import ContactUs from 'ContactUs';
import AboutUs from 'AboutUs';
import Tab2 from 'Tab2';
import Tab3 from 'Tab3';
import BottomTabNavigator from './BottomTabNavigator';
import {store} from '../store';
const RootStack = createStackNavigator();
export default function Navigator() {
const language = useSelector(languageSelect);
const {theme, colors} = useTheme();
const isDarkMode = useDarkMode();
const [isLoggedInValue, setIsLoggedInValue] = useState(false);
useEffect(() => {
i18n.use(initReactI18next).init({
resources: BaseSetting.resourcesLanguage,
lng: language ?? BaseSetting.defaultLanguage,
fallbackLng: BaseSetting.defaultLanguage,
});
if (Platform.OS === 'android') {
StatusBar.setBackgroundColor(colors.primary, true);
}
StatusBar.setBarStyle(isDarkMode ? 'light-content' : 'dark-content', true);
console.log('<<<<< IS LOGGED IN STATE >>>>>>');
console.log(isLoggedInValue);
console.log('<<<<< IS LOGGED IN STATE >>>>>>');
setIsLoggedInValue(store.getState().isloggedin.isLoggedIn);
}, [colors.primary, isDarkMode, language, isLoggedInValue]);
return (
<DarkModeProvider>
<NavigationContainer theme={theme}>
<RootStack.Navigator
mode="modal"
screenOptions={{
headerShown: false,
}}>
{!isLoggedInValue ? (
<>
<RootStack.Screen
name="Loading"
component={Loading}
options={{gestureEnabled: false}}
/>
<RootStack.Screen name="Walkthrough" component={Walkthrough} />
<RootStack.Screen name="SignIn" component={SignIn} />
<RootStack.Screen name="SignUp" component={SignUp} />
<RootStack.Screen
name="ResetPassword"
component={ResetPassword}
/>
</>
) : (
<>
<RootStack.Screen
name="BottomTabNavigator"
component={BottomTabNavigator}
/>
<RootStack.Screen name="Feedback" component={Feedback} />
<RootStack.Screen
name="ChangePassword"
component={ChangePassword}
/>
<RootStack.Screen name="ProfileEdit" component={ProfileEdit} />
<RootStack.Screen name="ContactUs" component={ContactUs} />
<RootStack.Screen name="AboutUs" component={AboutUs} />
</>
)}
</RootStack.Navigator>
</NavigationContainer>
</DarkModeProvider>
);
}
Signin.js
import React, {useState} from 'react';
import {
View,
TouchableOpacity,
KeyboardAvoidingView,
Platform,
Alert,
} from 'react-native';
import {useDispatch} from 'react-redux';
import {BaseStyle, useTheme} from '#config';
import {Header, SafeAreaView, Icon, Text, Button, TextInput} from '#components';
import styles from './styles';
export default function SignIn({navigation, route}) {
const {colors} = useTheme();
const [loading, setLoading] = useState(false);
const [id, setId] = useState('');
const [password, setPassword] = useState('');
const dispatch = useDispatch();
/**
* call when action onLogin
*/
const onLogin = () => {
setLoading(true);
dispatch(setIsLoggedIn(true));
navigation.navigate('Home');
setLoading(false);
};
const offsetKeyboard = Platform.select({
ios: 0,
android: 20,
});
return (
<View style={{flex: 1}}>
<Header
title="sign in"
renderLeft={() => {
return (
<Icon
name="times"
size={20}
color={colors.primary}
enableRTL={true}
/>
);
}}
onPressLeft={() => {
navigation.goBack();
}}
/>
<SafeAreaView style={BaseStyle.safeAreaView} edges={['right', 'left']}>
<KeyboardAvoidingView
behavior={Platform.OS === 'android' ? 'height' : 'padding'}
keyboardVerticalOffset={offsetKeyboard}
style={{flex: 1}}>
<View style={styles.contain}>
<TextInput onChangeText={setId} placeholder="username" value={id} />
<TextInput
style={{marginTop: 10}}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry={true}
value={password}
/>
<Button
style={{marginTop: 20}}
full
loading={loading}
onPress={onLogin}>
'sign_in'
</Button>
<TouchableOpacity
onPress={() => navigation.navigate('ResetPassword')}>
<Text body1 grayColor style={{marginTop: 25}}>
'forgot_your_password'
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
</View>
);
}
5
navigation/BottomTabNavigator.js
export default function BottomTabNavigator() {
const {colors} = useTheme();
const font = useFont();
const design = useSelector(designSelect);
const BottomTab = createBottomTabNavigator();
const exportHome = () => {
return Home;
};
return (
<BottomTab.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
}}
tabBarOptions={{
showIcon: true,
showLabel: true,
activeTintColor: colors.primary,
inactiveTintColor: BaseColor.grayColor,
style: {borderTopWidth: 1},
labelStyle: {
fontSize: 12,
fontFamily: font,
paddingBottom: 4,
},
}}>
<BottomTab.Screen
name="Home"
component={exportHome(design)}
options={{
title: 'Home',
tabBarIcon: ({color}) => {
return <Icon color={color} name="home" size={20} solid />;
},
}}
/>
<BottomTab.Screen
name="Savings"
component={Tab2}
options={{
title: 'Savings',
tabBarIcon: ({color}) => {
return <Icon color={color} name="bookmark" size={20} solid />;
},
}}
/>
<BottomTab.Screen
name="Loans"
component={Tab3}
options={{
title: 'Loans',
tabBarIcon: ({color}) => {
return <Icon color={color} name="clipboard-list" size={20} solid />;
},
}}
/>
<BottomTab.Screen
name="Profile"
component={Profile}
options={{
title: 'Profile',
tabBarIcon: ({color}) => {
return <Icon solid color={color} name="user-circle" size={20} />;
},
}}
/>
</BottomTab.Navigator>
);
}
I couldn't see the second tab navigator in your code but i saw that you create navigators inside a component which is in my opinion and experience is not what you supposed to do. you should create your navigators seperately not inside another component. in the documentation of react-navigation they show it this way so they should know what they are doing i guess :D
Login and Main app screens used to managed by switch navigators but with react-navigation 5 they dumped the switch navigators and instead support ternary operators in the navigator components like this
const RootStack = createStackNavigator<RootStackParamList>();
export const RootNavigator = () => {
const isLoggedIn = useReduxSelector(({ authState }) => authState.isLoggedIn);
return (
<RootStack.Navigator screenOptions={{ headerShown: false }}>
{!isLoggedIn ? (
<RootStack.Screen
name="AuthNavigator"
options={{ headerShown: false }}
component={AuthNavigator}
/>
) : (
<>
<RootStack.Screen name="TabNavigator" component={TabNavigator} />
<RootStack.Screen
name="DetailsNavigator"
component={DetailsNavigator}
/>
<RootStack.Screen
name="UserStackNavigator"
component={UserStackNavigator}
/>
<RootStack.Screen name="Announcements" component={Announcements} />
<RootStack.Screen name="Read" component={Read} />
<RootStack.Screen
name="Search"
options={{
...TransitionPresets.FadeFromBottomAndroid,
gestureEnabled: false,
}}
component={Search}
/>
</>
)}
</RootStack.Navigator>
);
};
After logging in and dispatching the appropriate action for your redux to change isLoggedIn state Navigator automatically strips out the Login page and fallbacks to what is under it. and at this point user cannot go to login or register screens even if they go back as many times as they want because we ripped the screens out of the navigation tree.
If you want to return to Login screens you just log out the user and navigation tree again contains these screens and you can navigate to them by navigation.navigate function with respective keys of your screens.
And also if you're curious what my tab navigator looks like it's just like this
const Tab = createBottomTabNavigator<TabParamList>();
//
const tabNavigatorScreenOptions: (props: {
route: RouteProp<TabParamList, keyof TabParamList>;
navigation: unknown;
}) => BottomTabNavigationOptions = ({ route }) => ({
tabBarIcon: (params) => <TabBarIcon {...params} route={route} />,
tabBarButton: (props) => (
<Pressable
{...props}
style={[props.style, { marginTop: Layout.dimensions.xs_h }]}
/>
),
// tabBarLabel: (props) => <TabBarLabel {...props} route={route} />,
});
export const TabNavigator = () => {
const debug = useReduxSelector(({ info }) => info.debug);
// Logger.debug = debug;
return (
<Tab.Navigator
tabBarOptions={{ adaptive: true, showLabel: false }}
screenOptions={tabNavigatorScreenOptions}
>
<Tab.Screen name="Discover" component={Discover} />
<Tab.Screen name="Library" component={LibraryNavigator} />
<Tab.Screen name="Settings" component={SettingsNavigator} />
{debug && <Tab.Screen name="TestNavigator" component={TestNavigator} />}
</Tab.Navigator>
);
};
Code is in typescript but that shouldn't be a problem about the logic.
if you have any questions further i am happy to help!
Happy coding my fellow react-native developer!

Ways to stop re-rendering when a state is changed ? (React Native)

Let me summarize a lil bit, what I'm trying to do is when the news is clicked, a modal (in this case I used a library: react-native-modalize https://github.com/jeremybarbet/react-native-modalize) is presented with the respective details of the clicked news
In order for the content inside the modal to be dynamic, I used states to store the details, so whenever a click is registered on the news, a series of details is passed thru function parameters and store in the respective state
THE PROBLEM is that whenever a state on the top level is changed the whole component refresh itself, and it's causing problem like:
Scenario 1: when the user scroll until the end of the scroll view and pressed on one of the news, modal is brought up and because the state is being changed, the whole app refreshes and the scroll view jumps back to the top.
app.js
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { useRef, useState, useEffect } from 'react';
import { AppLoading } from 'expo'
import { StyleSheet, Text, View, FlatList, SafeAreaView, ScrollView, Image, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { Modalize } from 'react-native-modalize';
import { Ionicons } from '#expo/vector-icons';
import { render } from 'react-dom';
import Header from './components/Header';
import NuaDaily from './components/Daily';
import Content from './components/Content';
import moment from "moment";
const HomeStackScreen = () => {
const [title, setTitle] = useState([])
const [author, setAuthor] = useState([])
const [publication, setPublication] = useState([])
const [imageUrl, setImageUrl] = useState([])
const [summary, setSummary] = useState([])
const modalizeRef = useRef(null);
const onOpen = (title, author, publication, imageUrl, summary) => {
modalizeRef.current?.open();
setTitle(title)
setAuthor(author)
setPublication(publication)
setImageUrl(imageUrl)
setSummary(summary)
};
return (
<>
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
<View style={styles.container}>
<Header />
<NuaDaily modalize={onOpen} style={styles.nuadaily} />
<Content modalize={onOpen} />
</View>
</ScrollView>
</SafeAreaView>
<Modalize snapPoint={650} modalTopOffset={10} ref={modalizeRef} style={{ width: '100%', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
<View style={{padding: 20}}>
<Image
style={{width: 350, height: 200, zIndex: 1000, borderRadius: 8, marginTop: 10}}
source={{ uri: `${imageUrl}` }}
/>
<Text style={styles.modalTitle}>{title}</Text>
<View style={styles.detailsBar}>
<Text style={styles.modalAuthor}>{author}</Text>
<Text style={styles.modalPublication}>{moment(publication).format("MM-DD-YYYY")}</Text>
</View>
<Text style={styles.modalSummary}>{summary}</Text>
</View>
</Modalize>
</>
)
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
tabBarOptions={
{
activeTintColor: '#00B2FF',
inactiveTintColor: 'gray',
showLabel: false,
style: { height: 60, borderRadius: 0, backgroundColor: 'rgba(255, 255, 255, 0.85)'}
}
}
showLabel = {false}
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Home') {
return <HomeIcon />
}
},
})}
>
<Tab.Screen name="Home"
component={
HomeStackScreen
} />
</Tab.Navigator>
</NavigationContainer>
);
}
});
Daily (index.js) (imported on app.js)
import React from 'react';
import { useState, useEffect } from 'react';
import {View, Text, FlatList, Image, ImageBackground, ScrollView, SafeAreaView, TouchableOpacity} from 'react-native';
import axios from 'axios';
import moment from "moment";
import * as Font from 'expo-font';
import AppLoading from 'expo-app-loading';
import { useFonts } from 'expo-font';
import styles from './style';
import Dot from '../../assets/images/dot.svg';
const nuaDaily = ( props ) => {
const Item = ({ title, author, publication, imageUrl, summary }) => (
<View style={styles.item}>
<TouchableOpacity onPress={()=>props.modalize(title, author, publication, imageUrl, summary)}>
<Image
style={{width: 210, height: 200, zIndex: 1000, borderRadius: 8, marginTop: 10}}
source={{ uri: `${imageUrl}` }}
/>
<Text style={ [styles.title, {fontFamily: 'Poppins'}]}>{title}</Text>
<View style={styles.bottomBar}>
<Text style={styles.author}>{author}</Text>
<Text style={styles.publication}>{moment(publication).format("MM-DD-YYYY hh:mm")}</Text>
</View>
</TouchableOpacity>
</View>
);
const renderItem = ({ item }) => {
if(moment(item.publication).isSame(moment(), 'day')){
return <Item title={item.title} author={item.newsSite} publication={item.publishedAt} imageUrl={item.imageUrl} summary={item.summary} />
}else{
// return <Item title={item.title} author={item.newsSite} publication={item.publishedAt} imageUrl={item.imageUrl} summary={item.summary} />
}
};
const [data, setData] = useState([])
useEffect(() => {
axios.get(APIURL)
.then((res) => {
setData(res.data)
})
.catch((err) => {
console.log(`error calling API ${err}`)
})
},[])
return (
<View style={styles.container}>
<View style={styles.containerTitle}>
<Dot style={styles.dot} />
<Text style={ [styles.PrimaryText , {fontFamily: 'Quicksand'}]}>NUA Daily</Text>
</View>
<FlatList
showsHorizontalScrollIndicator={false}
horizontal={true}
data={data}
renderItem={renderItem}
style={styles.flatList}
/>
</View>
)
}
export default nuaDaily;
Demo of the problem (Scenario 1)

Why is React Native App.js navigation undefined in App.js?

export default function App ({navigation})
There is a function that starts with and contains <Stack.Navigator>.
But when I want to use the navigation feature and use "navigation.navigate ('Lead') in onPress
TypeError: undefined is not an object evaluating react navigation
I get an error. My English is not very good. Please help me on this topic. Now I'm adding the sample codes to you.
import * as React from 'react';
import { StyleSheet, AsyncStorage, Alert, View, Image, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { ApplicationProvider, Layout, Input, Button, Text, Spinner, IconRegistry, Icon } from '#ui-kitten/components';
import { EvaIconsPack } from '#ui-kitten/eva-icons';
import * as eva from '#eva-design/eva';
import LinkingConfiguration from './navigation/LinkingConfiguration';
import * as axios from 'axios';
import Base64 from './components/Base64';
import LeadScreen from './screens/LeadScreen';
import AddLeadScreen from './screens/AddLeadScreen';
import TabBarIcon from './components/TabBarIcon';
import HomeScreen from './screens/HomeScreen';
import CampaignsScreen from './screens/CampaignsScreen';
import MenuScreen from './screens/MenuScreen';
const Stack = createStackNavigator();
export default function App({ navigation }) {
const logout = async () => {
await AsyncStorage.removeItem('token');
dispatch({ type: 'SIGN_OUT' });
};
return (
<>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider {...eva} theme={eva.light}>
<Layout style={styles.container}>
<AuthContext.Provider value={authContext}>
<NavigationContainer linking={LinkingConfiguration}>
<Stack.Navigator>
{state.isLoading ? (
// We haven't finished checking for the token yet
<Stack.Screen name="Splash" component={SplashScreen} options={{ headerShown: false }} />
) : state.token == null ? (
// No token found, user isn't signed in
<>
<Stack.Screen name="Login" component={LoginScreen} options={{ title: 'Oturum Açın' }} />
</>
) : (
// User is signed in
<>
<Stack.Screen name="User" component={UserScreen} options={{
headerStyle: { backgroundColor: '#e8a200' },
headerTintColor: 'white',
headerTitleStyle: { color: '#fff' },
headerRight: () => (
<TouchableOpacity activeOpacity={0.4} underlayColor='transparent' onPress={() => logout() } style={{ marginRight: 12 }}>
<Icon
style={styles.icon}
fill='#FFFFFF'
name='power-outline'
/>
</TouchableOpacity>
)
}} />
<Stack.Screen name="Lead" component={LeadScreen} options={{
title: 'Müşteri Adayları',
headerStyle: { backgroundColor: '#e8a200' },
headerTintColor: 'white',
headerTitleStyle: { fontWeight: 'bold', color: '#fff' },
headerRight: () => (
<TouchableOpacity activeOpacity={0.4} underlayColor='transparent' onPress={ () => console.log(navigation) } style={{ marginRight: 12 }}>
<Icon
style={styles.icon}
fill='#FFFFFF'
name='plus-outline'
/>
</TouchableOpacity>
)}} />
<Stack.Screen name="AddLead" component={AddLeadScreen} options={{ title: 'Müşteri Adayı Ekle' }} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
</AuthContext.Provider>
</Layout>
</ApplicationProvider>
</>
);
}
You can pass the navigation or rout prop into options in the stack.screen like this
options={({navigation})=>({
'Your code here'
})}
I have edited it with your code for you.
<Stack.Screen name="Lead" component={LeadScreen}
options={({ navigation }) => ({
title: 'Müşteri Adayları',
headerStyle: { backgroundColor: '#e8a200' },
headerTintColor: 'white',
headerTitleStyle: { fontWeight: 'bold', color: '#fff' },
headerRight: () => (
<TouchableOpacity activeOpacity={0.4}
underlayColor='transparent' onPress={() =>
console.log(navigation)} style={{ marginRight: 12 }}>
<Icon
style={styles.icon}
fill='#FFFFFF'
name='plus-outline'
/>
</TouchableOpacity>
)
})} />
First, I think the navigation here is unnecessary, you can remove it.
export default function App({ navigation }) {
In your screen components, ie. UserScreen, you can use navigation like so
function UserScreen({navigation}) {
// ...some codes
navigation.navigate('WHERE_TO_GO');
// ...some other codes
}
You could also use navigation without passing it to the props.
First you need to import useNavigation
import { useNavigation } from '#'react-navigation/native'
And then, in your component
function UserScreen() {
const nav = useNavigation();
// ...some codes
nav.navigate('WHERE_TO_GO');
// ...some other codes
}
You could get more details here https://reactnavigation.org/docs/use-navigation/
For the onPress function of Lead screen,
<Stack.Screen name="Lead" component={LeadScreen} options={({ navigation }) => ({
title: 'Müşteri Adayları',
headerStyle: { backgroundColor: '#e8a200' },
headerTintColor: 'white',
headerTitleStyle: { fontWeight: 'bold', color: '#fff' },
headerRight: () => (
<TouchableOpacity activeOpacity={0.4} underlayColor='transparent' onPress={ () => navigation.navigate('WHERE_TO_GO') } style={{ marginRight: 12 }}>
<Icon
style={styles.icon}
fill='#FFFFFF'
name='plus-outline'
/>
</TouchableOpacity>
)})} />
You should use useLinkProps to solve this issue:
import { useLinkProps } from '#react-navigation/stack';
And this is an example on how to write code which uses it:
const LinkButton = ({ to, action, children, ...rest }) => {
const { onPress, ...props } = useLinkProps({ to, action });
I have tried everything but this worked for me.
Make a function in your app.js and pass props then access using props.navigation like below.
function CustomDrawerContent**(props)** {
.....
logOut = () => {
AsyncStorage.clear();
**props.navigation.navigate("Login");**
};
.....
}

Categories

Resources