react-navigation / react-navigation

Routing and navigation for your React Native apps

Home Page:https://reactnavigation.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Stacknavigator transition extremely slow

natashache opened this issue · comments

Hi,

I use nested navigator structure and have noticed that the transition between some screens are very, very slow.

Here's the setup:

export const MainNavigator = TabNavigator({
  Search: { screen: Search},
  Explore: { screen: Explore },
  CourseNavigator: { screen: CourseNavigator },
  MyProfile: { screen: MyProfile }
}, {
  initialRouteName: 'CourseNavigator',
  navigationOptions: {
    tabBar: {
      visible: false
    },
    header: {
      visible: false
    }
  },
})

export const CourseNavigator = StackNavigator({
    MyCourses: { screen: MyCourses },
    PlayerNavigator: { screen: PlayerNavigator }
  }, {
  headerMode: 'screen'
})

export const PlayerNavigator = StackNavigator({
    CoursePage: { screen: CoursePage },
    Player: { screen: Player }
  }, {
    headerMode: 'screen',
    mode: 'modal'
})

So PlayerNavigator is nested under CourseNavigator, which is under MainNavigator.

What's moving very slow is the transition between the Mycourses screen in CourseNavigator and the CoursePage screen in PlayerNavigator. It takes two full seconds for the screen to change.

The code for the transitioning is very straightforward:

  goToCourse(course) {
    const {navigate} = this.props.navigation
    navigate('PlayerNavigator')
    this.props.setCurrentCourse(course)
  }

// And then in the render function...

        <Button transparent small
          onPress={ () => this.goToCourse(course) }>
          <Icon name='ios-arrow-forward' />
        </Button>

Wondering if anybody has experienced similar slowness and what could be the potential cause?

Wondering if anybody has experienced similar slowness

I am also facing similar issue now (iOS Simulator) , it used to be fast :s

Without reading to much into the issue, you might want to check debug > slow animations in your ios simulator. cmd+t activates it, and so you could have triggered it by accident when you meant to create a new tab in a browser for example.

@GertjanReynaert Thanks. Unfortunately this is not the issue. Slow animations is turned off. And I'm experiencing the same slowness on both ios simulator and iphone. In general, I've found the transition of stackNavigator to be slower than tabNavigator. But this particular transition mentioned above is slow to the extreme. If I don't find a solution, I'll just have to abandon this library all together, because this creates terrible user experience.

@natashache - I'm having similar performance issues and am able to workaround them by disabling Remote JS Debugging. I'm not seeing the issue with Hot or Live Reloading turned on.

Hope that helps.

@jurassix thanks for the suggestion. Unfortunately this is not related to remote debugging. The slowness is present no matter whether I use remote debug or not.

Having the same, TabNavigator is fast as flash but StackNavigator like kind a have delay before pushing screens. Same on pressing the Back button, but have no issues when swiping back.

Behaving like doing a lot of calculations on JS thread, perf monitor also show tremendous FPS drop when pushing screens.

  • tested on iOS only
  • RN - 0.42.3
  • RNav - beta7
  • using with redux

#706 may be related

I dont think it's related @nico1510

I have performance problems on simulator too, here are some key points;

  • Have 3 StackNavigators nested in TabNavigator with 3 tabs.
  • Have a redux action call in one of StackNavigator route's componentWillUnmount that resets the component to initialState.
  • If I comment out this line it seems to fast a bit. But the reset action is very light, don't think it has impact, doing fine with NavigationExperimental
  • I think componentWillUnmount doesn't fire immediately and if I navigate to another screen before it fires, the StackNavigation becomes completely unresponsive. Logs shows that it fires the navigate action but doesn't mount the component so touch responders are not responding.
  • TabNavigator still works when these freezes happen.

I'll try to make an isolated project to show this.

I agree that it navigates very slow.

Even with a simple setup that only includes one or two views, that navigates to each other or to themselves. Even with something as simple as the one in this test app: https://github.com/cbrevik/react-navigation-test

The Tab bar and the header are horribly slow to update on Android. Slow, even on a real device with a release build 😞

I have a Stack -> Tab -> Stack nesting and using Icons from native-base

ios is better

Any help would be appreciated !

On Android it even renders page twice

I'm getting the same slowness on Android. iOS is decently fast, but Android takes has 2+ second delays

I have a particularly slow transition on a single screen (iOS), which is rendering a chart with svg (but this is on the js thread, so why would be a problem?).

I'm facing the same problem on a real android device...

Same here, in Android device. The crazy thing is that when I have Remote JS Debugging enabled it goes way faster!

Same here, on iphone 7, without Debug Remote JS and when I press a button, on 'headerRight' to navigate to another screen, twice it open the next screen two times and stack them. So when I get back once, it gets back to the page it should have been all along.

@haaswill you are facing to different problems.
The double/multiple tap on the button can be fixed by adding a custom navigationHelpers such as the one by @microwavesafe in #271
The bad performance without Debug JS Remote, in my own experience is due to the use of console.log, but could be different in your case.

@arnaubennassar I don't have any console.log. I'm waiting new versions to see if the problem is solved :/

I'm having the delay even in RELEASE build of Android. Device is Samsung Galaxy S7 Edge.

demo: https://im2.ezgif.com/tmp/ezgif-2-9ec91aa14b.gif

notice that when it's delayed, no animation was played

Same here, in the iOS simulator everything is super fast without any delay (with or without remote JS debugging enabled) but when I run it on my iPhone 6s there is at least 500ms delay before navigation push.

I gave it another shot after a month, with 1.0.0-beta.9 i have no issues right now. Using 3 different routers with tab - stack nested navigations and its performing very well

@sercanov can you please tell me which version of React and React-native you are using? Downgrading to *.beta.9 for me alone didn't do the trick...

@WillyRamirez
React 15.4.2
RN 0.42.3
react-redux 4.4.5

I dont know what but i think there is a minor bug creates the performance issues in some cases.

You could look at components lifecycle events, like doing state-heavy processes in componentWillMount or in scene transitions.
Also check your navigation reducers and immutable conversions.

@sercanov I went back to version 0.43.0 since im using flatlists i can't go back further, however, I tried removing the FlatLists and it now works on emulator, still slight delay on phone.

I'm using version 1.0.0-beta.11 and it's terribly slow on Android. On iOS it's decently fast, but definitely not on Android.

I got the same problem T_T

If you're having problem where tapping a buttons responds slow or the screen requires a tap to transition in certain scenarios - see @jurassix comment to turn off remote debugging. it solves the problem

I solve my problem by unbinding the navigation reducer from my HOME page witch contains a large list. The reason of my problem is every time I navigate it will update my HOME page then make the translation between every screens slow.

@philsam This happens with a release build.

I don't have any console.logs and performance is slower on an Android emulator and much slower on an actual device. However, if I do remote debugging on either the emulator or physical device, performance noticeably increases.

I have the same Issue on release with RN@0.45 on android. It's very fast on emulator but slow on device. Do you have any solution ? Use an other navigator maybe ?

Same issue here :( , I have a delay of ~1s when navigate between tab in tabnavigator :(
Upgrading to 0.45 is a solution? Is there a workaround?

I have a Tab Navigator with a nested Stack Navigator. The stackNav pushes the screen after a solid 1.5 seconds. No FPS drops. I can still use the parent in that time window. If I push the same screen again, they open one after the other.
react-native 0.45.1
react-navigation beta.11

Removing all console.log made it little bit faster.
or putting this is at start of app.

if((process.env.NODE_ENV || '').toLowerCase() === 'production'){
  // disable console. log in production
  console.log = function () {};
  console.info = function () {};
  console.warn = function () {};
  console.error = function () {}
  console.debug = function () {}
}

@matart15 your tips look like to work. It's much faster with all logs deactivated !

Problem in release build without console logs.

Hello, I was having the same issue. Switching between tabs lagged heavily on my real android device, whether connected to a remote debugger or not. It should also be noted that swiping between screens caused no lag, but pressing the tab buttons did (by a very noticeable amount).

I solved it by turning off "JS Dev Mode". I did this by going to "Dev Settings" and unchecking "JS Dev Mode".

Yea I did what @BenjaminSnoha did, went to Dev Settings and unchecked "JS Dev Mode", then I reloaded the app and everything works as one would expect.

@matart15 solution did wonders for me. In prod and in development.

@meilers It just replaces console functions with empty function in production. If you have so many console.log in many places it might be lazy way to disable them all at once. you can remove if()syntax. Just the idea.

For me, the problem seemed to be another component, namely react-native-simple-markdown. Once I got rid of that, I got rid of the lag. It seems the navigator wants to load the whole component into memory before pushing it.

I am getting same issue, its very slow :(

commented

Currently working on an App, using Tab and Stack navigation.
After upgrading:

react-native from 0.42.3 to 0.45.1
react-native-navigation from 1.0.0-beta.7 to 1.0.0-beta.11

the experience went down, it takes like one second or more to navigate from a view to another, especially on iPhone6, (iPhone7 and Android are a not extremely changed)

In RemoteDebug mode, navigating is fast and smooth. Release and Debug are slow.
Diagnosed most of the components, removed console.log and listeners that may cause the issue, not change.

As also @samdoj mentioned, it seems to load and then navigate.

I experienced the issue described by @BenjaminSnoha on Android real device (Nexus 5) and Android emulator with RN 0.45.1 and react-navigation 1.0.0-beta.11. Tabs transition by clicking on tabs were extremely slow (swipe was not affected). The DrawerNavigator was also affected.

The slowness issue disappeared completely after:

  • turning off JS Dev Mode on the app Dev Settings
  • disable all console.log for NODE_ENV=production in index.android.js using @matart15 solution
  • export NODE_ENV=production before launching the packager server (react-native start)
  • disable Debug JS Remotely (so I have no output at all on remote-redux-devtools window ...)

Performance on my app was improved (still not great) by making liberal use of componentShouldUpdate. In that lifecycle method, I checked if the the nextProps's navigation prop was going to that screen. If not, I returned false. By doing this, I prevented many unnecessary render calls. Because the navigation prop is passed to every screen by react-navigation, the render method of every screen will be called whenever the user navigates to a new screen (even if that screen is not navigated to/from!). These extra render calls were causing the UI thread on the Android Device to lag, causing some performance issues.

@nlbrown2 I too tried getting nextProps's navigation prop in componentShouldUpdate(). But how did you checked if it is being called during transaction animation or after setState() or navigator.setParams().

@matart15 I am not sure of the timing of when the navigation prop is updated. The project I am working on uses redux, so our navigation reducers update state for the navigation prop, and so once that updates then every component that has the navigation prop will be updated (regardless of if they are currently displayed or not). I would assume that the navigation prop is updated as soon as the reducer returns (which should be decently fast).

@nlbrown2 thank you

I was working on the same problem as @amed-kino and we just found the cause: in our case, it was in fact related to Sentry (react-native-sentry module). During the transition, some error occured and Sentry took a long time to parse the stacktrace, blocking the thread. Disabling sentry removed the delay. So if you use Sentry, this might be it. We still don't know exactly what the error was or why Sentry is taking so long.

@amed-kino I have the same experience in RemoteDebug mode, it's fast and smooth only when it's enabled. Any luck to find the root of the problem?

commented

cc @sertony
As @buesing mentioned here, getsentry/sentry-react-native#158
The issue was Sentry that we were using, which collect information for each view change for error tracking (called Stacktrace). If you are invoking some type action during the transition, check it out.

@amed-kino @buesing thank you! In fact I use sentry in the project. I'll check it.

I'm also having this problem in what is a pretty basic app so far. Below is a demonstration of what is happening with debug off vs debug on: (Debugging clicks are active so you can watch from when I first tap the element)

Debug Off

debugoff

Debug On

debugon

I understand that but this issue has randomly appeared after working fine for a few weeks and I'm not sure how to track this issue down without using the debugger. I've spent the last day or so making performance optimisations which have affected both modes but not enough to be usable.

Okay, so I have some good news. I had seen above that removing console.log statements from your app to improve performance. After reading this I came across this react-native performance guide.

I followed the instructions there and thought that would have been enough, I recompiled my app with that setup and it actually made no difference at all and I figured my issue was not with console.log statements and pressed onwards.

Getting desperate I decided to retry but by manually commenting out every trace I made in my code, that has completely removed the delay and has actually benefitted from the optimisations I had made whilst trying to figure this thing out.

Hope this helps somebody!

Should we try RNRF to solve this issue?

I just wanted to say that my problem doesn't seem to lie in the implementation itself, but rather in a library I was using called react-native-simple-markdown. react-navigation seems to get bogged down if it has to create a lot of views, and for some reason that library produces a view for every word of text.

Hi guys, since a lot of people have reported this issue in many places (ex. #2540, #2531, #740, etc.) I’ve decided to close the duplicates and try to help you guys here, since it’s the oldest one.

As a brief heads up, I want to clarify that I’ll propose a way to “address it” for the current version (v1.0.0-beta.12) and I’ll leave this issue open for further discussion - we are aware that performance lib-side can get better, hopefully in v1.0 we’ll find an “embedded way” to help with it.

In my experience, the issue with degrading performances when using StackNavigator is not strictly caused by the navigator itself but by the components it needs to render - in particular, two behaviours can cause huge drops:

  1. Since it’s a stack, all the “cards” below the one currently rendered can get re-rendered too if not handled carefully. (See life-cycle method shouldComponentUpdate).

  2. Crossing the bridge between the Native and the JS cores is heavy on performances

What does it all mean?
Let’s make an small example: you have a stack nav that is rendering its first card, A. On A you have a button that navigates to B, meaning that B will transition over A and will become the visualized screen.

(point 1)
If both A and B are connected to a redux store, let’s call it the userInformations, and B changes an information contained there (ex. The user’s surname), then if A and B are standard Components they both will be re-rendered, and this is not ideal.
A way to avoid this is to implemented, in the lifecycle method shouldComponentUpdate, something that roughly can looks like this:

  shouldComponentUpdate(nextProps) {
    if (nextProps.navigation.stackNav.index === 0) {
      // NOTE WELL: THIS IS A ROUGH CUT CONDITION
      // MAKE SURE TO IMPLEMENT IT PROPERLY
      // IN YOUR COMPONENT
      
      return true;
    }
    return false;
  }

An “easy” way to benchmark it is to add a console.log method in both A and B render() methods.

(point 2)
Let’s say that B has a Map component with 50 Markers that gets parsed from a remote server. A Map is an heavy component, and the way it handles its markers is not really efficient (react-native-maps/react-native-maps#369), and a network request is “heavy” too.
Let’s say that, since you want to have it all to show right away, you start the network request in A, in the same onPress method that triggers the navigation; and that in the render method in B simply returns the Map with the .map method on the markers array. This is quite heavy on performance, and it may very well explain why the transition is slow; a lighter approach would be to:

  1. In the onPress method in A, only navigate to B
  2. On the constructor for B, set the state to have a loading:true condition
  3. On the componentDidMount method in B, trigger the network request method for fetching the markers, so that it looks something like
    this.props.fetchMarkers().then(()=>{this.setState({loading: false})});
  4. In the render() method of B, have something along these lines:
render() {
  if (this.state.loading) {
    return (
      <MapView
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0.0922,
          longitudeDelta: 0.0421
        }}
        cacheEnabled
      />
    );
  }

  return (
    <MapView region={this.state.region} onRegionChange={this.onRegionChange}>
      {this.props.markers.map(marker => (
        <MapView.Marker
          coordinate={marker.latlng}
          title={marker.title}
          description={marker.description}
        />
      ))}
    </MapView>
  );
};

Try doing “one thing at the time”.

Moreover, here are a few comments that I have found across these issues that may provide you with different insights on how to approach the perf drops:

Again, we are aware that perfs can be improved on lib side too; we are redacting a new roadmap for v1, so look forward to that.
In the meantime, I hope this comment that help you all out.
(last tip: tools like react-native-debugger or reactotron can help with finding out which calls are most time consuming)

I also described IMHO the correct way to fix this issue in react-navigation, a StaticContainer that prevents updates to non-active screens, with an extra condition to allow components to receive an update when their active state changes (ie: the last active scene gets one update when a different scene gets focus so it can do any defocus cleanup it wants to) and receive an update if their params change.
#706 (comment)

@kelset thanks for taking the time to provide those suggestions.

You suggest using if (nextProps.navigation.stackNav.index === 0) { return true; } as the logic for specifying that a component should update, but you say "NOTE WELL: THIS IS A ROUGH CUT CONDITION, MAKE SURE TO IMPLEMENT IT PROPERLY IN YOUR COMPONENT". Can you clarify what you mean by this? Are you saying that we can't rely on nextProps.navigation.stackNav.index having the correct value for us to base this logic on?

And when you say "make sure you implement it properly in your component", I'm assuming you just mean that if your component has other additional logic that it could/should use in shouldComponentUpdate (e.g. a variable myFooName being updated), then we should make sure we include that additional logic as well rather than just always returning true when nextProps.navigation.stackNav.index === 0 regardless of any other conditions.

Being only a couple months new to React Native I also would like an answer to @jordanmkoncz 's comment above--in particular how to properly implement shouldComponentUpdate.

@kelset Could you please give an example of what would be a "proper" implementation? Additionally, I'm a little unclear why we would want a re-render if the index of the nextProps navigation state is 0. Doesn't that mean it will re-render card A or am I completely off?

Cheers!

Hi guys, sorry for taking so long but I was on vacation... and this answer will be long again I'm afraid.

First off, yes @dantman I saw your original comment about StaticComponent while going through all the issues; I think it's probably a good approach, but I'm not 100% sure - it's very hard for a navigation library to define how much of these "decisions" should be taken by the lib and how much by the single coder imho.
That said, a PR with an implementation of it would surely be appreciated ;)

Moreover:

  • Yes @jordanmkoncz, you can trust the value in nextProps.navigation.stackNav.index and yes, what you wrote in your second paragraph:

if your component has other additional logic that it could/should use in shouldComponentUpdate (e.g. a variable myFooName being updated), then we should make sure we include that additional logic as well rather than just always returning true when nextProps.navigation.stackNav.index === 0 regardless of any other conditions.

Is exactly what I meant 💯

  • @Hauuguu a "better" (=more complex) version of the implementation for the shouldComponentUpdate should be something along the lines of:
  shouldComponentUpdate(nextProps, nextState) {
    if (
      (!_.isEqual(this.state.localTabNavigation, nextState.localTabNavigation) ||
        !_.isEqual(this.props.isFetching, nextProps.isFetching)) &&
      nextProps.navigationLocal.eventTabBar.index === 1
    ) {
      return true;
    }
    return false;
  }

Which, again, simply means that we render based on the additional logic/data we show in that particular component (in this case, there's a tab bar with another nested tab bar and a fetching boolean).

In the example shouldComponentUpdate I made in my previous comment, that method should be pasted inside component A -> A will be re-rendered only when the stack index is at 0, which means that it will re-render only when A is shown.


All these things said I want to stress again that before using this method you should carefully read learn and understand the whole React lifecycle.

Here are a few more articles to help you out (in no particular order):

I'm using React Navigation as part of React-Native-Router-Flux and was having these slowdowns related to the Stack Navigator after updating to React-Native 0.48.4. 2 second load times. BG not fully loaded during the transition animation. Changing debug to release mode in my xcode run scheme fixed it for me.

In my case I was experimenting huge lags when going back with navigation depending of the case.
And I found out that I was using the following version in package :

"react-navigation": "^1.0.0-beta.11"
And found out that new version was out and changed it to :
"react-navigation": "^1.0.0-beta.13"

Now it works smoothly.
Maybe can help.

Thanks for clarifying, @kelset 😃

thanks @kelset for your ideas. Where does the navigation.stackNav prop come from? I just have state, dispatch, navigate, goBack and setParams.

navigation.stackNav in that context was basically assuming a complex redux + react-navigation example (inspired by this). It's not a standard prop you get from the library ;)

@kelset if an app using react-navigation doesn't use redux (or other similar frameworks), then the performance problem you mentioned at (point 1) (re-rendering multiple Card components) doesn't occur, right?

@exKAZUu uhm I am not completely sure since I never coded a complex app that didn't have redux.
My guts tell me that no, it shouldn't happen.

But to be fair you can simply check by attaching some console logs to the render methods ;)

@kelset I see. Thanks!
Actually, I’m now making a PR for fixing the problem (point 1).
When I tried to test whether my PR worked well or not before submitting it, I noticed I could not reproduce the problem in my app which doesn’t use redux. So I cannot test my PR now 😓
I plan to make a react-navigation+redux project reading the article you mentioned in order to reproduce the problem, but if there is an easier way (e.g. some sample react-navigation+redux projects) to reproduce it, please share the way.

For what it's worth, this is how we've worked around the problem for now:

const removeDuplicateRoutes = (state) => {
  if (!state.routes) return state

  let duplicateRoutesCount = 0

  const routes = state.routes.reduce((out, route, index) => {
    const cleanRoute = removeDuplicateRoutes(route)

    if (!index) {
      out.push(cleanRoute)
    } else {
      const prevIndex = index - 1
      const prevRouteName = out[prevIndex].routeName
      if (prevRouteName === route.routeName) {
        ++duplicateRoutesCount
        out[prevIndex] = cleanRoute
      } else {
        out.push(cleanRoute)
      }
    }

    return out
  }, [])

  // don't clone state, we want to keep references intact (at least at the top
  // level)
  state.routes = routes
  state.index -= duplicateRoutesCount

  return state
}

@joeybaker Naive question... Where are you putting that code?

@GollyJer We've got it in a "navigation" reducer. When we receive an action, we run the state through this before returning.

#2942
I've submitted a PR for solving (point 1) described in #608 (comment).
It's my first time to use Redux, but I hope my PR solves this issue (partially).

I just just started working with react-native/react-navigation and I was surprised at how stack navigator functioned. I made a Gist showing all of my points:

https://gist.github.com/RoryKelly/037c01916301798b4a5b8a40e0b3b87e#file-app-js-L8

I created an app that navigates through a series of screens, using redux and react-redux. I was suprised at how:

  • screens that are not visible are still mounted.
  • screens that are not visible can still be rendered.

I made a component that unmounts invisible components. It negatively affects animations but im working on that.

For me, creating a custom tabBarComponent resolved the issue. I experienced the below symptoms on the android simulator:

  • clicking on tab bar items would immediately play the animation on the tab bar button
  • only after some delay and after the animation finished would the redux navigation actions/logs show up on reactotron.
  • there was no delay between the navigation actions showing up in reactotron until the time the navigation to another tab actually occurred.

I'm unsure if this is even intended behaviour for the default TabBarBottom on android to wait for the button animation to complete before transitioning to the new tab...Anyhow, by setting tabBarComponent to a custom component in the navigationOptions resolved it for me. See: https://reactnavigation.org/docs/navigators/tab.

Same issue as @markHco with the navigation only completing after the tab icon has animated.. no issue when sliding.

The same question like @RoryKelly.

I have:

  • Main_page:
    100+ small components inside, each shows every property1..100. Quite heavy render.

  • Inner_page:
    Which just update one property, quite frequently, but it is just one property, but every update fires heavy Main_page.render.

I reread this thread, but still do not understand the reasons why it is default behavior.

Really do not like it :(

0.49.3 + 1.0.0-beta.19

Same issue here with StackNavigator, a simple navigation from screen A to screen B (both with static components, without fetching data e etc..) has a delay between 300ms ~ 500ms to start the navigation. I already disabled JS Dev Mode.

Quick Tip🤪: if you are using redux-logger, make sure createLogger is only added in __DEV__ mode, otherwise it will create a lot of reudx log, which make your app very slowly, even if you build a production package.

Good tip @sapjax. Here's what mine looks like. LOGGING_ON is my own override.

// Log state changes.  https://github.com/evgenyrodionov/redux-logger
const LOGGING_ON = false;
const debuggingInBrowser = !!window.navigator.userAgent;
if (ENV === 'development' && debuggingInBrowser && LOGGING_ON) {
  const { createLogger } = require('redux-logger');
  const logger = createLogger({
    collapsed: true,
    duration: false,
    timestamp: false,
    diff: true,
  });
  middleware.push(logger);
}

Also I no longer pass anything in ScreenProps. In my experience doing so significantly slows down navigation speed.

Do you have redux logger enabled in production build?
Here is a quick snippet you can use to enable logger in dev and have it off in production

import DefaultReduxLogger from 'redux-logger';

const MIDDLEWARES = [
    thunkMiddleware,
     __DEV__ && DefaultReduxLogger,
     __DEV__ && perfLogMiddleware,
].filter(Boolean);
const mainMiddleware = applyMiddleware(... MIDDLEWARES);

When removing all logging and still have performance issues, check your mapStateToProps functions in your containers. Most certainly you return every time it gets called some kind of new object.

I've changed from Stack to TabNavigator
I had to set visible false in all tabs and disable swipe either

StackNavigator is really slow while Tab Navigator is really fast

I hope its usefully

After setting the duration to 0 in the transitionConfig the lag went away :

const AppScreens = TabNavigator(
    Home: {
      screen: Home,
    },
    Profile: {
      screen: Profile,
    },
  },
  {
    initialRouteName: "Home",
    swipeEnabled: false,
    animationEnabled: false,
    lazy: true,
    transitionConfig: () => ({
      transitionSpec: {
        duration: 0,
      },
    }),
  },
);

but you are using TabNavigator, there is no lag in TabNavigator. The problem occurs when using StackNavigator

Why stack navigator is too fast (at 60fps) in iOS device (and simulator) but drop frames and slow (2 seconds delay) on android? What must I do for android performance?

Try following 5 things -:
1> Add activeOpacity with value 1 because if you set alpha JS thread send this too main UI thread and hence the delay.
Ex ->

Title of Button

2> Add your component will mount code inside Interaction manager It will run your JS code after the transition will complete
Ex ->InteractionManager.runAfterInteractions(() => {
});

3>set lazy to true for tab bar, it will help in not reloading unwanted controllers/components
Ex ->
const tabNavigatorConfig = {
tabBarPosition: 'bottom',
tabBarOptions: {
},
mode: 'card',
lazy:true,
}

4> Remove all logs, and comment logger middleware if u r using.
5> Run in release mode.

if someone can provide me with an app that reproduces this problem, that would be helpful. otherwise we can't do much. it's quite possibly related to logging, as mentioned above. another possibility is lots of large images. I need an app where this is happening to help.

Logging is likely not the sole cause. We already know that Stack Navigator's implementation is problematic, every navigation change (even a setParams for a single screen) triggers the render() of every single route, even though all but 1-2 of them are invisible, irrelevant, and don't actually care about the change to navigation data.

That adds up, even if you aren't using heavy components the render() of a screen component is typically not negligible because it's responsible for defining all the views for the layout of a screen. And that will inevitably add up when you have a bunch of routes in the stack. PureComponent doesn't get around this because navigation changes each time the state changes. You'd need a very specific shouldComponentUpdate to ignore irrelevant data when not the active screen, which shouldn't be necessary.

Fixing this (finding a non-hacky way to not re-render screens unless they are active, were active in the previous state, or had their params changed) will likely be the thing that solves this bug for most people.

every navigation change (even a setParams for a single screen) triggers the render() of every single route

I am new to working on this project, can you elaborate on this @dantman? I added console.log to render methods on the Simple Stack example and only one log fired any time I pushed a new screen. How can I repro this?

@brentvatne It's been awhile, I'll have to try an examine the current behaviour myself.

@brentvatne I'll send you as invitation to a repo in bitbucket. A very basic code that uses react-navigation with extremely slow transitions in an android release or without debug mode

@andfs - the project that you invited me to, "trending", doesn't have react-navigation installed in it, it just has a google sign in screen

@brentvatne sorry, the right code is in develop branch

@andfs - you're using 1.0.0-beta.9 and react-native 0.43 here.. these are both very old (about 1 year). please update to the latest version before reporting bugs. additionally, the app doesn't compile

@brentvatne this bug report was opened about a year ago and until now there are interactions in this issue, so there are people facing this problem yet. You asked for an example of project with this problem and that was the only one that I have.
Tomorrow I'll update. If there is no problem I'll share it with evrybory to use as example ;)

I wanted to make an update. I was using a package called react-native-simple-markdown which makes a new for every word which caused it to use a lot of memory. The problem resolved when I switched to react-native-markdown-view. My point here is not about specific libraries, but rather about memory. It seems that react-navigation has trouble transitioning to and from scenes that have deeply nested components like this. I'm thinking it has something to do with measurements. Bottom line: Make sure you and your packages are using memory efficiently for best responsiveness.

I had similar issues that ended up being caused by a delay in TouchableOpacity used for the nav buttons. Switching to TouchableHighlight made it instant.