React Native - React Navigation slow transitions when nesting navigators
Solution 1
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;
Solution 2
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.
Solution 3
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(()=>{})
Solution 4
i had same problem, for me it helped using NativeStackNavigator instead of StackNavigator
Related videos on Youtube
Shivansh Singh
Updated on February 11, 2022Comments
-
Shivansh Singh about 2 years
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" },
-
Sara Inés Calderón about 6 yearsdid you ever find a solution?
-
Shivansh Singh about 6 yearsno .. but a work around worked .. I delayed the rendering of component by 10ms using timeout function on componentWillMount .. it still shows JS thread drop but UI doesn't get affected due to it. Maybe it is bad coding or react navigation nesting issue.
-
-
Shivansh Singh about 5 yearsI don't have the means to confirm your answer but I also think this will work as I said delay of 10ms before pushing the screen fixed the lag so it is also doing same. Thanks for the answer.
-
TheEhsanSarshar almost 5 yearsawesome solution
-
Oliver D about 4 yearsHey @Carlos should i use
InteractionManager.runAfterInteractions(()=>{})
in every single Screen, I'm faced slow transition issue on it? -
Jitender about 4 years@OliverD: I would say use it where you have your routes defined.
-
Oliver D about 4 years@Carlos Interest!, I'm open an issue here and someone else faced the same issue maybe it's a bug?
-
Vivek sharma almost 3 yearswell it worked for me, have you tried interactionManager?
-
BruceHill about 2 yearsThe blog mentioned in this answer does not exist any longer. Here is the wayback machine link to it: web.archive.org/web/20201127164304/https://www.novemberfive.co/…