React Native Nested Stack Navigation issue - reactjs

TL;DR Please excuse me, for the mess I've made here and If something is unclear
I made the following react native stack navigation
import { NavigationContainer, createNativeStackNavigator, useNavigation } from '#react-navigation/native'
import { Pressable, Text } from 'react-native'
const LoginStack = createNativeStackNavigator()
const hideHeader = { headerShown: false }
const LoginNavigation = () => {
return (
<LoginStack.Navigator initialRouteName="SignInScreen">
<LoginStack.Screen name="SignInScreen" component={SignInScreen} options={hideHeader}/>
<LoginStack.Screen name="OTPScreen" component={OTPScreen} options={hideHeader}/>
</LoginStack.Navigator>
)
}
const LoginScreen = () => (
<View>
<View>...Some Other UI code is here</View>
<LoginNavigation />
</View>
)
const ProfileScreen = () => {
const navigation = useNavigation()
return (
<View>
<Pressable onPress={() => navigation.navigate('LoginScreen')}> 👈🏻
<Text>Go to Login </Text>
</Pressable>
</View>
)
}
const RootStack = createNativeStackNavigator()
const RootNavigation = () => {
return (
<RootStack.Navigator initialRouteName="LoginScreen">
<RootStack.Screen name="LoginScreen" component={LoginScreen} options={hideHeader}/>
<RootStack.Screen name="ProfileScreen" component={ProfileScreen} options={hideHeader}/>
</RootStack.Navigator>
)
}
const App = () => {
return (
...other provders
<NavigationContainer>
<RootNavigation />
</NavigationContainer>
)
}
The navigation flow goes like this
LoginScreen -> (SignInScreen -> OTPScreen) -> ProfileScreen
The problem is that every time I dispatch "LoginScreen" navigation from "ProfileScreen," it goes to "OTPScreen" instead of "SignInScreen." Yes, that does sound strange, but after numerous tests, the result remains the same.
I'm positive the reason is the router cache, but I'm not sure how to solve it. Tried clearing cache by following few answers here but no luck
I've figured out a way to fix this and it's functioning 👇🏻
navigation.reset({index: 0, routes: [name: 'LoginScreen']})
but it is moving forward (but it must go backwards like goback)
Can someone help me with this, or is there a better way to do this?

Related

How to pass params from a stack navigator to a Material top tab navigator?

I have a Stack named as PaymentStack which consists of two Stack screens. The second screen consists of a MaterialTopTabs which have two other Screens named as 'Net banking' and 'Cash Payment'.
PaymentStack consists of two screens, one screen is a simple Stack screen navigator with a functional component passed in the component attribute of Stack.Screen but for the other screen I have tried to make a Material Top tabs Navigator
const TopTabStack = createMaterialTopTabNavigator();
//this MaterialTopTab is passed as a component in the below PaymentStack tab navigator
const MaterialTopTab = ({ navigation }) => (
<TopTabStack.Navigator>
<TopTabStack.Screen
name={'Net banking'}
component={ComponentX} //*I want to receive the params in this screen/cmpnt.
/>
<TopTabStack.Screen
name={'Cash payment'}
component={ComponentX}
/>
</TopTabStack.Navigator>
)
//this PaymentStack is passed as a component in Bottom tab navigator
const PaymentStack = ({navigation}) => {
<Stack.Navigator>
<Stack.Screen
name={'PaymentOption'}
component={ComponentY} //Suppose I am on this Screen
/>
<Stack.Screen
name={'MaterialTopTab'}
component={MaterialTopTab} //this component is defined above
/>
</Stack.Navigator>
}
export default function AppStack = () => {
return (
<NavigationContainer>
<PaymentStack /> //defined above
</NavigationContainer>
);
}
Now suppose I am in the screen named PaymentOption whose component is ComponentY and I want to navigate to the Screen named as Material top tab whose component is a Material top tab navigator in which two screens are present named as Net banking and Cash payment and receive the params in the Net banking. How can we do this ?
I tried
navigation.navigate('MaterialTopTab', {body : data} )
But when I am accessing the body in ComponentX , it shows undefined.
function ComponentX({route}){
console.log(route.params.body)
.......
.......
}
Ok, hopefully this is what you're looking for. Please ignore the typescript typings if you aren't using Typescript. As you can see, our RootStack component is a stack navigator that contains 1 screen and a TabNavigator. I don't have experience using top tabs but I'm pretty sure it works the same way as bottom tabs. We can navigate to our TabNavigator from the first screen in RootStack (Screen1), passing a prop called helloKitty to the TabNavigator. The TabNavigator then passes the helloKitty prop from route.params to the first screen in the TabNavigator (TabScreen1). I'm not particularly sure if what I did with TabScreen1Props is the proper way to do it, but it works for now. Honestly though, at this point, if this is what your navigation structure looks like, why not consider useContext or a state management library like Zustand?
import React, { useEffect } from 'react'
import { Text, View } from 'react-native'
import { createStackNavigator, StackScreenProps } from '#react-navigation/stack'
import { BottomTabScreenProps } from '#react-navigation/bottom-tabs'
import { Button } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
type HelloKittyType = string
type RootStackParamList = {
Screen1: undefined
SomeCuteTabs: { helloKitty: HelloKittyType }
}
type RootStackNavigationProps = StackScreenProps<RootStackParamList>
type SomeCuteTabsParamList = {
TabScreen1: undefined
TabScreen2: undefined
}
type SomeCuteTabsNavigationProps = BottomTabScreenProps<SomeCuteTabsParamList>
const RootStack = createStackNavigator<RootStackParamList>()
const SomeCuteTabs = createStackNavigator<SomeCuteTabsParamList>()
const Screen1 = ({ navigation }: RootStackNavigationProps) => (
<View>
<Button title="Go to tabs" onPress={() => navigation.navigate('SomeCuteTabs', { helloKitty: 'I love hello kitties' })} />
</View>
)
interface TabScreen1Props extends SomeCuteTabsNavigationProps {
helloKitty: HelloKittyType | undefined
}
const TabScreen1 = ({ route, navigation, helloKitty }: TabScreen1Props) => {
useEffect(() => {
console.log(helloKitty)
}, [])
return (
<View>
<Text>Tab Screen 1</Text>
</View>
)
}
const TabScreen2 = () => (
<View>
<Text>Tab Screen 1</Text>
</View>
)
const RootStackComponent = () => (
<RootStack.Navigator>
<RootStack.Screen name="Screen1" component={Screen1} />
<RootStack.Screen name="SomeCuteTabs" component={SomeCuteTabsComponent} />
</RootStack.Navigator>
)
const SomeCuteTabsComponent = ({ route, navigation }: RootStackNavigationProps) => (
<SomeCuteTabs.Navigator>
<SomeCuteTabs.Screen name="TabScreen1">{(props) => <TabScreen1 {...props} helloKitty={route.params?.helloKitty} />}</SomeCuteTabs.Screen>
<SomeCuteTabs.Screen name="TabScreen2" component={TabScreen2} />
</SomeCuteTabs.Navigator>
)
export default function App() {
return (
<NavigationContainer>
<RootStackComponent />
</NavigationContainer>
)
}

React navigation - switch between 2 tab navigation

Friends, I tried many times but I could not do it. How can I switch between 2 tab navigation, for example 1. tab navigation become a member and login, 2. tab navigation homepage, settings etc. I was doing it in version 3 but now for some reason I couldn't, thanks to all the friends who helped
here is a quick snippet, i only show one tab navigator's full logic for reference, but the second can follow the same logic.
Using react navigation v5.x.x
It works by nesting navigators in screens
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createStackNavigator } from '#react-navigation/stack';
const HomeTabsNavigator = createBottomTabNavigator();
const HomeTabRenderer = () => {
const initialRouteName = 'Feed';
const initialRouteParams = {};
return (
<HomeTabsNavigator.Navigator
initialRouteName={initialRouteName}
initialRouteParams={initialRouteParams}
>
<HomeTabsNavigator.Screen name={'Feed'} component={'<add react component>'} />
<HomeTabsNavigator.Screen name={'Dashboard'} component={'<add react component>'} />
<HomeTabsNavigator.Screen name={'Friends'} component={'<add react component>'} />
</HomeTabsNavigator.Navigator>
);
};
const Stack = createStackNavigator();
const StackRenderer = () => {
const initialRouteName = 'FirstTabNav';
const initialRouteParams = {};
return (
<NavigationContainer>
<Stack.Navigator initialRouteName={initialRouteName}
initialRouteParams={initialRouteParams}
headerMode={'none'}
>
<Stack.Screen name={'FirstTabNav'} component={HomeTabRenderer} />
<Stack.Screen name={'SecondTabNav'} component={OtherTabRenderer} />
</Stack.Navigator>
</NavigationContainer>
)
}

How to know drawer state inside react Component onStateChange V5 in react-navigation

I would like to know when the drawer was opened/closed on onStateChange (in V4 I could get it from the state, but in V5 I can't find this info). Or any other idea as long as I stay with the component context
App.js
const Drawer = createDrawerNavigator();
function MyDrawer(props) {
return (
<NavigationContainer onStateChange={props.onStateChange}>
<Drawer.Navigator drawerType="front" drawerPosition="left">
<Drawer.Screen name="Properties" component={PropertiesTabs} />
<Drawer.Screen name="Profile" component={Profile} />
</Drawer.Navigator>
</NavigationContainer>
)
};
export default with(MyDrawer)
With.tsx
export function with(Component: any) {
class PNDContainer extends React.Component {
child: any;
componentDidMount() {
//debugger;
console.log('PND Mounted - First Time Screen');
}
componentWillUnmount() { }
render() {
const myHookValue = this.props.myHookValue;
debugger;
return (<Component onStateChange={(state) => {
debugger;
console.log('Screen Changed');
}} />)
}
}
return PNDContainer;
}
I know that it's possible to implement with functional components (but that's not what I need).
I followed couple of examples with no luck How to Use React Hooks in Class Components
onStateChange={(state) => {
const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
}
Another option is to use the fiber node tree find the DraverView type and check if it's open

React Native - pass props from One screen to another screen (using tab navigator to navigate)

I need to pass data from my HomeScreen to my SecondScreen. There are a ton of examples of how to do this if i'm clicking a button on the HomeScreen to navigate to SecondScreen, but can't find anything showing how to pass to SecondScreen if I'm using a v2 bottom tab navigator to go from HomeScreen to SecondScreen. I've tried screenprops and a couple other methods and spent about 8 hours trying to figure it out but could not get it to work. Any idea how to do this? Please, any hint would be amazing. Here is my code:
MainTabNavigator.js:
const config = Platform.select({
web: { headerMode: 'screen' },
default: {},
});
const HomeStack = createStackNavigator(
{
Home: HomeScreen,
},
config
);
HomeStack.navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({ focused }) => (
<MaterialIcons name="home" size={32} />
),
};
HomeStack.path = '';
const SecondStack= createStackNavigator(
{
Second: SecondScreen,
},
config
);
SecondStack.navigationOptions = {
tabBarLabel: 'Second screen stuff',
tabBarIcon: ({ focused }) => (
<MaterialIcons name="SecondScreenIcon" size={32} />
),
};
SecondStack.path = '';
const tabNavigator = createBottomTabNavigator({
HomeStack,
SecondScreen
});
tabNavigator.path = '';
export default tabNavigator;
HomeScreen.js:
class HomeScreen extends Component {
constructor(props){
super(props);
}
componentDidMount(){
this.setState({DataFromHomeScreen: 'my data that Im trying to send to SecondScreen'})
}
//....
SecondScreen.js:
class SecondScreen extends Component {
constructor(props){
super(props);
}
render()
return(
<View>{this.props.DataFromHomeScreen}</View>
)
//....
****Please find THINGS I'VE TRIED below:****
HomeScreen.js: when i do this it receives it at first but then passes null
render(){
return(
<View>
//all of my home screen jsx
<SecondScreen screenProps={{DataFromHomeScreen : 'data im trying to pass'}}/>
</View>
)
}
MaintTabNavigator.js: when i do this it receives it at first but then passes null
HomeStack.navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({ focused }) => (
<MaterialIcons name="home" size={32} />
),
};
<HomeStack screenProps={{DataFromHomeScreen:'data im trying to pass'}}/>
HomeStack.path = '';
I've tried like 5 other ways too that I can't even remember at this point. I don't want to have to call my database again in the second screen to get user info. Nobody I know knows react or react native. The React Native documentation at https://reactnavigation.org/docs/en/stack-navigator.html is minimal at best, only showing the below:
const SomeStack = createStackNavigator({
// config
});
<SomeStack
screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>
even if you go to the examples in the documentation and search for the word 'screenprop' you will not see any mention of the screen prop feature in either of the examples. All questions that I've seen only address how to pass props on button click which is easy. Is what I'm trying to do possible? I'm sure I'm not the only person using tab navigator who's retrieved data in the homescreen and need to pass it to other screens . Any advice helps. thanks.
ps.
Here is my Sign in class that is calling the Home screen:
class SignInScreen extends React.Component {
static navigationOptions = {
title: 'Please sign in',
};
render() {
return (
<View
style={styles.container}
contentContainerStyle={styles.contentContainer}>
<View>
<SocialIcon
title='Continue With Facebook'
button
type='facebook'
iconSize="36"
onPress={this._signInAsync}
/>
</View>
);
}
_signInAsync = async () => {
let redirectUrl = AuthSession.getRedirectUrl();
let result = await AuthSession.startAsync({
authUrl:
`https://www.facebook.com/v2.8/dialog/oauth?response_type=token` +
`&client_id=${FB_APP_ID}` +
`&redirect_uri=${encodeURIComponent(redirectUrl)}`,
});
var token = result.params.access_token
await AsyncStorage.setItem('userToken', token);
await fetch(`https://graph.facebook.com/me?fields=email,name&access_token=${token}`).then((response) => response.json()).then((json) => {
this.props.navigation.navigate('Home',
{
UserName : json.name,
FBID : json.id,
email : json.email
});
}) .catch(() => {
console.log('ERROR GETTING DATA FROM FACEBOOK')
});
};
}
export default SignInScreen;
I think you're calling your database in componentDidMount in your HomeScreen component, (I'm right?) and because another component in the same hierarchy needs the same data, you should considerer wrapping this into a new component and do the call to your data in that father component, then you pass the data to all the children that needs it. This is the react way to do things. The state of HomeScreen should not have the data, your data should live in a parent component in a higher hierarchy and pass the data to children as props.
In this way when you create your tabs you can pass the props as the react native docs suggest:
import { createBottomTabNavigator, BottomTabBar } from 'react-navigation-tabs';
const TabBarComponent = (props) => (<BottomTabBar {...props} />);
const TabScreens = createBottomTabNavigator(
{
tabBarComponent: props =>
<TabBarComponent
{...props}
style={{ borderTopColor: '#605F60' }}
/>,
},
);
Another solution could be to use a global state management with Redux or something similar.
I hope this helps.
Edit:
class Home extends React.Component{
constructor(props){
super(props);
this.state = {data: null}
}
componentDidMount() {
//get your props from navigation (your facebook credentials)
//your call to database
this.setState({data: yourResponseData});
}
render(){
const TabNavigator = createBottomTabNavigator(
{
HomeScreen: props =>
<HomeScreenStack
{...this.state.data}
/>,
SecondStack: props =>
<SecondStack
{...this.state.data}
/>,
},
);
return(
<TabNavigator />
)
}
}
const App = createAppContainer(Home);
export default App;
Use this.props.navigation.navigate.
In your HomeScreen, once you have the data you want to send, then navigate over to SecondScreen like so:
this.props.navigation.navigate('Second', { data: yourData })
To access this in SecondScreen whenever it is navigated to using navigation props, you can use NavigationEvents along with this.props.navigation.getParam.
/* your imports */
import { NavigationEvents } from 'react-navigation';
export default class SecondScreen extends React.Component {
/* your methods and properties */
render() {
<View>
<NavigationEvents
onDidFocus={() => this.setState({ data: this.props.navigation.getParam('data', {}) })}
/>
{ /* your SecondScreen render code */ }
</View>
}
}
Edit: For example, with your SignInScreen implementation, to access the props, use:
const username = this.props.navigation.getParam('UserName', '')
const fbid = this.props.navigation.getParam('FBID', 0)
const email = this.props.navigation.getParam('email', '')
This is the basic approach that I am using:
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const TestComponent = (props) => {
return <Text>{`TestComponent: ${props.name}`}</Text>;
};
const Home = () => {
const Tab = createBottomTabNavigator();
return (
<View style={{flex: 1}}>
<Tab.Navigator>
<Tab.Screen name="Screen 1">
{() => <TestComponent name="test 1" />}
</Tab.Screen>
<Tab.Screen name="Screen 2">
{() => <TestComponent name="test 2" />}
</Tab.Screen>
</Tab.Navigator>
</View>
);
};
Notice that to pass the props to a Screen I am using a child function instead of passing a value to component. The child function can then return the component you want in the syntax you are used to, that has the props available. In this case, the props are the simple name, but you can expand this to handle your state.
I ended up using Redux, it only took me like 100 read throughs and attempts to learn it, but once I learned it it's amazing and simple.

Undefined navigator.push React-native 0.43.4

I am using the Navigator component of react-native but i still get error when i want to push to anthor page push undefined is not a function so there is my code
import React, { Component } from 'react';
import {
View,
StyleSheet,
Navigator,
Text,
TouchableHighlight
} from 'react-native';
import Home from './Home';
import Main from './Main';
class MainApp extends Component {
_navigate(){
this.props.navigator.push({
name: Home
})
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={ () => this._navigate() }>
<Text>GO To View</Text>
</TouchableHighlight>
</View>
);
}
}
and Home component
class Home extends Component {
render() {
return (
<View style={styles.container}>
<Text>Welcome Hello</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default Home;
I still get this error, I am a beginner in react-native so help me please ? why react-native so hard ?
After some hours of work, i resolve my problem
First i create MainApp and i define a Navigator with initialRoute my code look like this
class MainApp extends Component {
renderScene(route, navigator) {
console.log(route, navigator);
if (route.component) {
return React.createElement(route.component, { navigator });
}
}
render() {
return (
<Navigator
initialRoute={{name: 'Home', component: Home}}
configureScene={() => {
return Navigator.SceneConfigs.FloatFromRight;
}}
renderScene={this.renderScene}
/>
);
}
}
and after in Home screen i use this function
_navigate() {
this.props.navigator.replace({
name: 'Main',
component: Main
});
}
So now my project work well , the key is create a screen route hope it useful
So ! First of all, RN is not so hard ^^ I started two weeks ago don't worry it will become easier !
I'll give you an example if you're ok with that !
MainApp
renderScene(route,nav) {
switch (route.screen) {
case "LaunchScreen":
return <LaunchScreen navigator={nav} />
case "LoginScreen":
return <LoginScreen navigator={nav} />
case "ListBillScreen":
return <ListBillScreen navigator={nav} />
case "AddBillScreen":
return <AddBillScreen navigator={nav} />
case "BillScreen":
return <BillScreen navigator={nav} bill={route.props}/>
case "PersistanceDemo":
return <PersistanceDemo navigator={nav} />
}
}
render () {
return (
<Navigator
initialRoute={{screen: 'LaunchScreen'}}
renderScene={(route, nav) => {return this.renderScene(route, nav)}}
/>
)
}
}
You set all your "Routes" in the mainApp okay?
After that, if you want to navigate between views in others .js, you need to do this way :
this.props.navigator.push({ screen: 'AddBillScreen' });
You need to navigate with the name of your screen ! Once you've done that, it will be soooo easy for you to navigate between views you will see !
Keep strong !

Resources