Possible navigation issue in React Native/Redux ap

2019-03-13 18:08发布

问题:

During navigation in big React Native app with using Redux all visited scenes (scenes from navigation stack) are staying mounted. All these scenes receive props and get rendered in the order they were visited when any action is dispatched from last scene component. It causes freezes and visible delays between dispatching and last scene rendering.

For navigation I am using react-native-router-flux but same issue happens with original React Native Navigator too.

Video Possible navigation issues in React Native/Redux app

Code react-redux-navigation-test

Would be nice to know how to prevent passing props to not focused components from the navigation chain.

At the moment I am checking in shouldComponentUpdate of each component if this one is focused(visible) and return false in opposite case.

Is there any better solution?

回答1:

I recommend that you calculate the scene distance between the current scene and scene being rendered using the navigation index in the scene and header rendering methods. If the distance > 1 return null.

This should prevent rendering the entire navigation history. It doesn't make any sense to me that this kind of behaviour is not available out of the box, but there it is.



回答2:

I have not used the react-native-router-flux, however I do have experience with a fairly large React Native and Redux app. I have noticed that sometimes if the data you are working with gets large enough it can cause noticeable delays, but those are mostly limited to working in development. When the app is built it is usually noticeably faster. It seems as though Redux does things a little differently in development and production modes.

Regarding scenes in the navigation stack still being mounted, that is by design. Even when you navigator.push to another screen those previous screens remain mounted until they are either popped off the currentRouteStack or replaced from the currentRouteStack.

Also, it's probably worth noting that your simulator is in Slow Animations mode. Not sure if you did that for the video sake or not, but the navigation slowness is in part due to that. Just a heads up in case that wasn't on purpose.

I would check how the app seems to function after you've built it and it's not in development mode before troubleshooting the performance issues much further. May not be an issue for the final app product.



回答3:

I'm going to go out on a limb here but are you using one of the below methods to prevent re-rendering?

  • PureComponents (added in React 15.3 I think)
  • Manually with shallow compare props and state with shouldComponentUpdate method.

By default React will re-render all components upon update unless you correctly handle the shouldComponentUpdate.

I'm doing something similar and not having these issues at all



回答4:

We solved it by simply wrapping all screens in a component.

We have one ScreenView component that wraps the whole screen, and we pass it a parameter "restrictRerendersToRoutes".

This screen is connected to the state, and we update the currentScrene in our state and expose it to this screen view.

Then we simply restrict rerenders when the screen is in the background with this shouldComponentUpdate implementation:

  shouldComponentUpdate(nextProps) {
    if (!_.empty(this.props.restrictRerendersToRoutes)) {
      return !!this.props.restrictRerendersToRoutes
        .find((routeKey) => routeKey === nextProps.currentScene.name);
    }

    return true;
  }


回答5:

The problem is indeed that the whole navigation stack is connected to the store( because components are not unmounted unless you use resetto or resetnavigation )

For what it's worth here is what I do right now. I have been working with a slightly modified react redux implementation that skips updates for scenes that are not in view ( see below )

To use it you need to:

Store the route for one component tree in context

We created a wrapper for this

const CurrentRoute = React.createClass({ 
childContextTypes : { currentRoute: PropTypes.object },
 getChildContext() {
 return { currentRoute: this.props.route } 
},
render() { return this.props.children; } 
})

And use it in render scene

<CurrentRoute route={route}><CurrentScene navigate={navigate} route={route} index={index} /></CurrentRoute>

Then you can access the route a component has been rendered into.

Store the navigation stack in a singleton We use this code in configure scene

let routeStack = [];
export const updateRouteStack = stack => routeStack = stack;

Then you can use this slightly modified react-redux connect function, it will skip updates if component is rendered in another component tree then the currently displayed one ( or a similar implementation ) https://github.com/reactjs/react-redux/compare/master...ganmor:master

It might be possible to package this but I haven't had the time to look into it. If anyone feels like doing it.. Hope that helps