How to Reset the stacks on tab change inside bottomTabNavigayor? - react-native

Snack
I have bottomTabNavigator in my tab and inside each tab I have stacks. I want to reset the stack whenever I click on another tab.
Tab Navigator-
Tab 1 - |_Stack Navigator
- Screen 1
- Screen 2
Tab 2 - |_Stack Navigator
- Screen 3
- Screen 4
Tab 3 - |_Stack Navigator
- Screen 5
- Screen 6
The present scenario is, . Suppose I am on tab 1 - I navigate to Screen 2 from screen 1 . Then I click to Tab 2 . Now if I again click on Tab 1, Screen 2 is showing instead of Screen 1.
Similar thing is happening on each tab.
I want to reset the tab on each tab click.
Please help.
I am using -
"dependencies": { "#react-native-community/cli": "^4.1.0", "#react-native-community/masked-view": "^0.1.6", "#react-navigation/bottom-tabs": "^5.0.5", "#react-navigation/native": "^5.0.5", "#react-navigation/stack": "^5.0.5", "react": "16.9.0", "react-native": "0.61.5", "react-native-gesture-handler": "^1.6.0", "react-native-gifted-chat": "^0.13.0", "react-native-reanimated": "^1.7.0", "react-native-safe-area-context": "^0.7.3", "react-native-screens": "^2.0.0-beta.7", },

Update the latest version of react-navigation and its dependencies and use this solution, this worked for me.
<AppTabs.Screen
name="TabScreen1"
listeners={({ navigation }) => ({
tabPress: () => {
navigation.navigate('Main1', { screen: 'Main2' });
},
})}
/>

If you are using navigation version 4 or less, Then I have a very simple way to do it.
tabBarOnPress:({ navigation }) => {
navigation.navigate(HomeStack,{screen:navigation.state.routes[0].routeName});
navigation.popToTop();
},

You could ovverride tabBarOnPress to get control of the tab click and reset the stack manually or use some other tricks.
A possible solution should be
navigationOptions: ({ navigation }) => ({
tabBarOnPress: ({ scene, jumpToIndex }) => {
const { route, focused, index } = scene;
if (focused) {
if (route.index > 0) {
const { routeName, key } = route.routes[1]
navigation.dispatch(NavigationActions.back({ key }))
}
} else {
jumpToIndex(index);
}
},
});
You may need to update this code for v5. (Same goes for solutions below)
Refer more solutions here

Related

Error: React Native Navigation 5x - route.params undefined

I'm not getting params in route props on the screen I'm going to.
I try to send data from one screen to another but it does not arrive in the props route.params
Here I leave the versions that appear in package.json
"dependencies": {
"#react-native-community/masked-view": "0.1.10",
"#react-navigation/compat": "^5.3.14",
"#react-navigation/drawer": "^5.12.4",
"#react-navigation/native": "^5.9.3",
"#react-navigation/stack": "^5.14.3",
"react-native": "~0.63.4",
"react-native-gesture-handler": "~1.8.0",
"react-native-reanimated": "~1.13.0",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.2",
}
In screen 1 I have this code
import { useNavigation } from '#react-navigation/core';
export default function Screen1() {
const navigation = useNavigation();
function goToScreen2(data) {
navigation.navigate('Screen2', { "data": data });
}
return (
<View>{{'Code here'}}</View>
)
}
And on the Screen2 I receive it as follows
export default function Screen2({ navigation, route }) {
useEffect(() => {
alert(JSON.stringify(route.params))
const data = route.params.data;
}, []);
}
Please, I already searched and tried everything and nothing works.
Included this post Getting "undefined" when passing params using react-navigation version 5?
But I can't get it to work, although I don't see it differently.
From already thank you very much.

react native access title of the current screen in nested drawer navigation

I have a react native app using react-navigation where i want my header to look like this with a title and subtitle.
I'm using native-base Header to achieve this. At the root of the application i have a tabs navigation and each tab has a drawer navigation of its own. Like below
For all the tabs there is separate Header component inside which the Title i have hard coded. The sub title is supposed to be the title of the current active screen similar to the drawerLabel or the title which are configured in the navigation options. But i'm not able to access the same in my header component. I even pass the navigation as a prop to the header but it contains the tab as the active route and not the initialRoute of the drawer as active route. This is how i use the header component
<MyHeader navigation={navigation} subTitle={''} openDrawer={this.openDrawer}></MyHeader >
Below is my Drawer config
const DrawerConfig = {
Screen1: {
screen: Screen1,
path: 'screen1',
navigationOptions: ({ navigation }) => ({
title: `Screen 1`,
drawerLabel: 'Screen 1',
}),
}, Screen2: {
screen: Screen2,
path: 'screen2',
navigationOptions: ({ navigation }) => ({
title: `Screen 2`,
drawerLabel: 'Screen 2'
}),
}
}
const DrawerNavigator = createDrawerNavigator(DrawerConfig, {
initialRouteName: 'Screen1',
contentComponent: CustomDrawerComponent,
});
const MyDrawerNavigator = createAppContainer(DrawerNavigator);
export default MyDrawerNavigator;
I have event tried creating stack for each of the screen and have the default header but i was not able to customize it by passing in a custom header component which the docs say should work.
const MyScreen1Stack = createStackNavigator(
{
Screen1: {
screen: Screen1,
}
},
{
navigationOptions: ({ navigation }) => ({
initialRouteName: 'Screen1',
title: 'Screen 1',
drawerLabel: 'Screen 1',
header: ()=> <MyHeader />,
heaMode: 'screen'
}),
},
);
Below are the dependencies i have
"native-base": "^2.13.8",
"react": "16.8.1",
"react-native": "0.61.2",
"react-native-gesture-handler": "~1.3.0",
"react-native-modal": "^11.4.0",
"react-native-reanimated": "~1.2.0",
"react-native-screens": "1.0.0-alpha.23",
"react-native-vector-icons": "^6.6.0",
"react-native-web": "^0.11.7",
"react-navigation": "^4.0.10",
"react-navigation-drawer": "^2.3.1",
"react-navigation-stack": "^1.10.2",
"react-navigation-tabs": "^2.5.6"
Any help will be highly appreciated.
const MyScreen1Stack = createStackNavigator(
{
Screen1: {
screen: Screen1,
navigationOptions: ({ navigation }) => ({
header:<CustomHeader navigation={navigation}/>
});
}
});
Using custom header to this way and pass navigation props to header so you can access navigation in custom header and you can pass title and subtitle as a props as well.
<CustomHeader title="this is title" subtitle="this is subtitle"/>

createMaterialBottomTabNavigator not working in ios

I have used 'react-navigation-material-bottom-tabs' for bottom tab navigation's which is working perfectly fine in android with ripple effect on tab click but ripple effect not working in ios ,
import * as React from 'react';
import Screen1 from './Screen1';
import Screen2 from './Screen2';
import { createAppContainer } from 'react-navigation';
import { createMaterialBottomTabNavigator } from 'react-navigation-material-bottom-tabs'; // <- notice where we import createMaterialBottomTabNavigator from
import { MaterialIcons } from '#expo/vector-icons';
const tabBarIcon = name => ({ tintColor }) => (
<MaterialIcons
style={{ backgroundColor: 'transparent' }}
name={name}
color={tintColor}
size={24}
/>
);
const screens = {
Screen1: {
screen: Screen1,
navigationOptions: {
tabBarIcon: tabBarIcon('photo-album'),
tabBarColor: 'blue' // <- set this to the color you want
}
},
Screen2: {
screen: Screen2,
navigationOptions: {
tabBarIcon: tabBarIcon('favorite'),
tabBarColor: 'green' // <- set this to the color you want
}
}
};
const config = {
headerMode: 'none',
initialRouteName: 'Screen1',
shifting: true, // <- notice this has been set to true
activeColor: 'white',
inactiveColor: 'black'
};
const MainNavigator = createMaterialBottomTabNavigator(screens, config);
export default createAppContainer(MainNavigator);
dependencies from package.json:
"dependencies": {
"react": "16.8.6",
"react-native": "0.60.4",
"react-native-gesture-handler": "^1.3.0",
"react-native-paper": "^2.16.0",
"react-native-svg": "^9.6.2",
"react-native-svg-icon": "^0.8.1",
"react-native-svg-uri": "^1.2.3",
"react-native-vector-icons": "^6.6.0",
"react-navigation": "^3.11.1",
"react-navigation-material-bottom-tabs": "^1.0.0"
}
Looking at your dependencies it looks like you haven't followed the instructions for installing react-navigation completely.
It would seem that you haven't installed react-native-reanimated it would make sense to install this as it is required for react-navigation.
You can install it as follows:
yarn add react-native-reanimated
or with npm
npm install react-native-reanimated
As you are using a version of react-native greater than 0.60.0 you should be able to rely on the automatic linking. However you may need to reinstall the pods. You can do this by opening a terminal at the root of your project and running the following commands.
$ cd ios
$ pod install
$ cd ..

What am i doing wrong with this stack navigator?

I am trying to teach myself react-native by building a new app, using Firebase as the backend. Currently I'm trying to get my head around react-navigation navigators to lay out the screens on my app before i add content.
The issue I'm having is that the stack navigator doesn't navigate. It renders the next screen, and adds it to the page, and then initiates the transition - but for whatever reason the transition just doesn't happen.
so the following events fire:
* willBlur (on the originating screen)
*onTransitionStart` (on the stack transition)
I've assumed i've done something wrong, so i've rewritten three or four times, and this final time i've boiled it down to the simplest possible iteration of the problem, just a stack navigator and two screens - and i'm getting the same issue.
I've gone through the documentation a few times, and i think at this point i've just been a massive idiot about this.
This is my entire app to show it
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { createAppContainer, createStackNavigator } from 'react-navigation';
class Main extends React.Component {
navigateToList() {
const { navigation } = this.props;
navigation.navigate('list');
}
render() {
return (
<TouchableOpacity
onPress={this.navigateToList.bind(this)}
>
<Text>Go To Listing Page</Text>
</TouchableOpacity>
);
}
}
class List extends React.Component {
render() {
const { refreshing } = this.props;
return (
<Text>LISTING PAGE</Text>
);
}
}
const MainStack = createStackNavigator({
mode: { screen: Main },
list: { screen: List }
}, {
initialRouteName: 'mode',
});
const App = createAppContainer(MainStack);
export default () => (
<App />
);
and my package.json has the following:
"react": "16.8.3",
"react-native": "0.59.3",
"react-native-firebase": "^5.4.0",
"react-native-gesture-handler": "^1.3.0",
"react-navigation": "^3.11.0"
At the moment this just sits showing the "main" screen, and doesn't navigate to the "list" screen. Also the button stops responding, so it looks like things start working but then never complete for some reason i can't explain.

React Native - React Navigation slow transitions when nesting navigators

I am building a cross-platform native application using react-native and using react-navigation for navigating to and from screens and managing navigation state using redux. The problem arises when I am nesting my navigators.
For example, I am using Stack Navigator as the default navigator for my app.
export const DefaultNavigate = new StackNavigator(
{
Login: {
screen: LoginScreen,
},
Home: {
screen: AppDrawerNavigate,
},
AppTabNav: {
screen: AppTabNavigator,
},
}
);
where my first screen is loginscreen and home screen is a drawer navigator.
const AppDrawerNavigate = new DrawerNavigator(
{
InProcess: {
screen: InProcess,
},
Machine: {
screen: Machine
},
Settings: {
screen: Settings
},
Logout: {
screen: Logout
},
ContactUs: {
screen: ContactUs
}
}
);
When the user clicks on the Machine in the Drawer Navigator I am navigating the screen to AppTabNav declared in DefaultNavigator.
const AppTabNavigator = new TabNavigator(
{
MachineList: {
screen: MachineList,
},
CalendarView: {
screen: CalendarView,
}
},
);
which is a tab navigator with two screens as the name suggests one is using listview to display list and the other is using the calendarview to display calendar. There are around only 30-40 items in my dataSource of listview so rendering them is a piece of cake for listview. But when there is navigation from any screen to Machine screen from DrawerNavigator there is lag of 1-2sec and js thread drops to -2.1 which is really slowing down the transition.
and if someone need the code for Machine screen in drawer navigator here it is,
componentDidMount() {
if(this.state.loaded)
this.props.navigation.dispatch({ type: MACHINE});
}
render() {
return <AppActivityIndicator />
}
the following is my reducer code which is handling navigation of the screen,
case types.MACHINE:
nextState = DefaultNavigate.router.getStateForAction(
NavigationActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'Home' }),
NavigationActions.navigate({ routeName: 'AppTabNav' })
]
}),
state
);
the following is the render method of MachineList screen in drawer navigator,
render() {
return (
<View style={styles.container}>
<AppStatusBar />
<ListView
initialListSize={10}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
enableEmptySections={true}
/>
</View>
);
}
Please help me out of this one. What am I doing wrong?
dependemcies
"dependencies": {
"native-base": "^2.3.1",
"react": "16.0.0-alpha.12",
"react-devtools": "^2.5.0",
"react-native": "0.47.1",
"react-native-calendars": "^1.5.8",
"react-native-vector-icons": "^4.3.0",
"react-navigation": "^1.0.0-beta.11",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",
"redux-persist": "^4.9.1",
"redux-thunk": "^2.2.0"
},
"devDependencies": {
"babel-jest": "20.0.3",
"babel-preset-react-native": "3.0.0",
"jest": "20.0.4",
"react-test-renderer": "16.0.0-alpha.12"
},
I was facing the same issue. There was a substantial delay while switching the screen. I found this very useful blog https://novemberfive.co/blog/react-performance-navigation-animations/
So the problem was
When a new screen is pushed, React Navigation will initially render it off-screen and animate it into place afterward. This means that when a complex screen with lots of components that easily takes a few hundred milliseconds to render is pushed
To fix this, I used InteractionManager. It basically gives you the callback once all the animation has been completed.
Following is what I have done to avoid delay and app was working fine after the fix. Hope this helps.
// #flow
import React, { Component } from 'react';
import { InteractionManager, ActivityIndicator} from 'react-native';
class Team extends Component<Props> {
state = {
isReady : false
}
componentDidMount() {
// 1: Component is mounted off-screen
InteractionManager.runAfterInteractions(() => {
// 2: Component is done animating
// 3: Start fetching the team / or render the view
// this.props.dispatchTeamFetchStart();
this.setState({
isReady: true
})
});
}
// Render
render() {
if(!this.state.isReady){
return <ActivityIndicator />
}
return(
// Render the complex views
)
...
}
}
export default Team;
I was encountering the same issue with my application. Similar to what other people have described, I have nested Stack and Drawer navigators. The lagginess for me was with the transition between screens in a nested Stack Navigator.
I tried using InteractionManager to resolve this, but it did not seem to make much difference. Eventually I found that building a simple timeout in to delay the rendering of large components made a huge difference.
So, I wrote this simple useIsReady hook which I now use in my screens:
import { useEffect, useState } from "react";
const useIsReady = () => {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
setTimeout(() => setIsReady(true), 100);
}, []);
return isReady;
};
export default useIsReady;
This is how I use the hook in my screens:
import React, { memo } from "react";
import { useTheme } from "react-native-paper";
import { View, ActivityIndicator } from "react-native";
import { useIsReady } from "hooks";
const BusyIndicator = () => {
const theme = useTheme();
return (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator size="large" color={theme.colors.primary} />
</View>
);
};
const Camera = () => {
const isReady = useIsReady();
if (!isReady ) {
return <BusyIndicator />;
}
return (
<> ... </>
);
};
export default memo(Camera);
I have found that this has made the world of difference, and my screen transitions are now completely smooth.
i had same problem, for me it helped using NativeStackNavigator instead of StackNavigator
I was facing the same issue. The following steps helped me greatly decrease lag time:
If you wrapped your components (especially the components in BottomTabBar) with redux's compose where you defined your screens, You will do well to remove that. This will greatly improve the smoothness and speed of transitions.
Just as #Carlos has highlighted above, use InteractionManager.runAfterInteractions(()=>{})
Optimize memory usage and performance (v5)
For those who use version 5 of react navigation
You will need to follow the installation instruction from react-native-screens first. After that using the following snippet before your navigation stacks are rendered (typically in an index.js or App.js file):
// Before rendering any navigation stack
import { enableScreens } from 'react-native-screens';
enableScreens();
I was facing the slow tab navigate issue, The slow issues occur when having nesting things like in my case
I have Drawer from react-navigation/drawer FIRST LEVEL
Second I have Tabs from '#react-navigation/bottom-tabs' under the drawer SECOND LEVEL
The third thing I have a stack (it has multiple screens) from #react-navigation/stack under the Tabs THIRD LEVEL
<Drawer.Navigator>
<Tab.Navigator>
<Stack.Navigator>
<Stack.Screen name="Home"></Stack.Screen>
<Stack.Screen name="Details"></Stack.Screen>
</Stack.Navigator>
<Stack.Navigator>
<Stack.Screen name="Task"></Stack.Screen>
<Stack.Screen name="Details"></Stack.Screen>
</Stack.Navigator>
.....
</Tab.Navigator>
</Drawer.Navigator>
So if I remove any one thing from above the slow issue was gone for example if I remove Drawer.Navigator so I have only two nested things Tab.Navigator and Stack.Navigator so issue was gone.
So what I did I remove Drawer and use a drawer from native base(you can use another drawer package).
<Tab.Navigator>
<Stack.Navigator>
<Stack.Screen name="Home"></Stack.Screen>
<Stack.Screen name="Details"></Stack.Screen>
</Stack.Navigator>
<Stack.Navigator>
<Stack.Screen name="Task"></Stack.Screen>
<Stack.Screen name="Details"></Stack.Screen>
</Stack.Navigator>
.....
</Tab.Navigator>
I know how pain is that I suffering it for a long time and didn't find any solution from the official react-navigation team, Hope Team will fix nesting issue soon, for now, you can use that solution.
Pass a callback function to predefined requestAnimationFrame(no import needed) method and it will automatically invoke that function once the animation is completed.
requestAnimationFrame(() => {
//enter code here
}
I am also facing this issue with stack navigator, Drawer navigator works better..
in stack if there're content of more than 100 lines with some imported component it start's lagging and delay on navigate.
i used in one of my stack screen InteractionManager to render component after animation
it worked, but when i come back on click back, animation shutters/lags
i checked out https://reactnavigation.org/docs/react-native-screens/
as it says react-navigation already uses native navigation component
for disabling and using RN View :-
import {enableScreens} from 'react-native-screens'
enableScreens(false)
i disabled it and now its working way better than native one, dont know why, currently i am in debugging mode, so cant say if it's cause of the remote view...
and really confused why it lags on native navigation while not in RN View
can anyone let me know why its happening? i want to use native navigation