React-native-router-flux: Router re-mount scene on lifecycle update

Created on 14 Sep 2017  路  6Comments  路  Source: aksonov/react-native-router-flux

Version

  • react-native-router-flux v4.0.0-beta.21
  • react-native v0.47.1

I have outside of the Router component other components (alerts). The behaviour appears when a state outside of the Router component changes.

Expected behaviour

Scene is not touched by lifecycle re-rendering

Actual behaviour

Scene will be un-mounted and re-mounted on each lifecycle update outside of the Router component, which do not affect them.

Steps to reproduce

I created a very simplified example project.

npm install && npm start

In the app, you can type a text into the TextInput, which is saved to the state of the SceneExample. If you click on the button below, the arrow function will call the setter of App and set new alert messages, which are rendered in Alert.

Should: React re-renders the Alert component with new props
Actual: React re-renders the Alert component with new props and remount the SceneExample component

This behaviour sound similar to #2382, but according to the issue author it is not.

Project: react-native-router-flux_issue_2382_example_2017_09_14.zip

Description

I have outside of the Router component other components (alerts). If their states are updated, the Router component will be re-rendered, but it Router re-mount the current scene, which followers to the lost of the current state. Seems to be very similar to the onPress event of our TextInput, @nicovak.

According to my experience, which happens when React components have no keys and React can not find the component on re-rendering. It creates a new component instance for the current rendering. I investigated the code to find the rendering of the component in the router scenes.

src/navigationStore.js:314 (processScene()) is the method where all scenes "get hooked". In src/navigationStore.js:368 starts the iteration over all scenes and in src/navigationStore.js:391 the scenes get wrapped by createWrapper() (see src/navigationStore.js:209). In my point of view this is necessary to pass the props to the scene component.

In createWrapper() are two options for stateless components and not stateless component. In both cased (see definitions below) I run into the stateless component path. I tried to add a extra key there for React at the definition of this instance, but without any change. What you should notice there is, that in both cases the returned component instance is wrapped by the props wrapper function, which is defined in src/navigationStore.js:303 as:

wrapBy = props => props

Thats the point where I stopped understanding the behavior if the code. Actually this helps to solve the issue.

Scene definition with stateless component

<Scene
    key="LandingEmail"
    component={(props) => (
        <SceneLandingEmail
        key="SceneLandingEmail"
        setAlertMessages={this.props.setAlertMessages}
        userSignedIn={this.props.userSignedIn}
        />
    )}
    hideNavBar={true}
/>

Scene definition without stateless component

<Scene
    key="LandingEmail"
    component={SceneLandingEmail}
    hideNavBar={true}
/>

Most helpful comment

I am having the exact same issue. Keys don't seem to make a difference with regard to re-rendering vs. completely remounting the component

All 6 comments

I am having the exact same issue. Keys don't seem to make a difference with regard to re-rendering vs. completely remounting the component

Short update: I built a workaround for this issue by storing the problematic states (in the example not in the App component), but in e.g. the Alert component (set via refs). This works, but is ugly code.

Due to other issues I decided to switch to react-navigation, which do not have this issue. Without this issue you will get a tremendous better performance. Due to this, I would highly recommend using another router until this issue is fixed.

It is not right usage of RNRF. RNRF creates needs navigational structures inside Router component, so it should be top-level component - you can put all alerts inside as Overlay component - check Example for 'message bar' alerts.

Have faced with that issue too.
The reason was

Actions.jump(sceneKeys[someScene]);

The fix is replacing it on

Actions.pop();

react-native-router-flux v4.0.0-beta.28
react-native v0.54.2

@aksonov Forcing Router to be the top-level component was a strange design choice. It really breaks the architecture of React Native that Router unmounts and remounts children components whenever a parent component of Router updates. Can you explain why this was a necessity for the library to work? Perhaps we can come up with a fix so that Router does not need to be top-level.

Bit of info for my use case: I want to wrap my router in a component like the following

class UserInteractionBlocker extends React.Component {
    render() {
        return (
            <View pointerEvents={this.props.active ? "none" : "auto"}>
                {this.props.children}
            </View>
        )
    }
}

Whenever an async call is made in the app, redux updates this.props.active so that the entire screen becomes disabled to the user. When the async call finishes, redux updates active again so the screen is once again enabled. Another option I considered would be to make this a BaseComponent and have all my components extend it; however, this doesn't solve the navBar issue (navBar buttons would still be interactable). For this reason, I need UserInteractionBlocker to be the parent of Router in order to block all user interaction.

Router unmounts and remounts children components whenever a parent component of Router updates

This is an old issue, but I feel that it is important to resolve. I don't understand the complexity for why this occurs.

@aksonov is it possible to elaborate?

I am be looking for a fix to stop the unmounting and remounting. I think it's fine for the Router to maintain it's own state independently.

React-navigation for example have the NavigationContainer (state manager) as the root node but the Router can appear lower down the tree https://github.com/react-navigation/react-navigation/blob/master/packages/native/src/NavigationContainer.tsx.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tonypeng picture tonypeng  路  3Comments

fgrs picture fgrs  路  3Comments

sarovin picture sarovin  路  3Comments

GCour picture GCour  路  3Comments

YouYII picture YouYII  路  3Comments