Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render available scenes based on app state #2824

Closed
matusholec opened this issue Jan 22, 2018 · 8 comments
Closed

Render available scenes based on app state #2824

matusholec opened this issue Jan 22, 2018 · 8 comments

Comments

@matusholec
Copy link

Version

Tell us which versions you are using:

Expected behaviour

I want to be able to render available scenes based on the app state stored in redux.
Something like:

class NavigationRouter extends Component {
  render() {
    const {appStarting, notLoggedIn} = this.props

    return (
      <Router>
        {
          appStarting && (
            <Stack key='root'>
              <Scene initial key='startup' component={StartupScreen} />
            </Stack>
          )
          notLoggedIn && (
            <Stack key='loggedOutWrapper'>
              <Scene initial key='welcomeScreen' component={WelcomeScreen} type='reset' />
              <Scene key='loginScreen' component={LoginScreen} />
              <Scene key='registerScreen' component={RegisterScreen} />
            </Stack>
          )
        }
      </Router>
      )
    }
  }
}

const mapStateToProps = state => ({
  appStarting: state.appStarting,
  notLoggedIn: !state.loggedIn,
})

export default connect(mapStateToProps)(NavigationRouter)

Actual behaviour

Using the above code I am getting:

There is no route defined for key startup.
Must be one of: 'welcomeScreen','loginScreen','registerScreen'.

This worked pretty well in v3 but trying to migrate to v4 it's not working (pretty sure I might be missing something obvious). Would really appreciate any kind of input.
Thanks a lot.

@mnogueron
Copy link

Hi @Matt33, have you been able to find a solution to your problem?
I'm in the same situation and I would be really interested if you have one!

@matusholec
Copy link
Author

matusholec commented Jan 30, 2018

Hey @mnogueron , I did a workaround that I thought would be the most beneficial in terms of keeping all the redirection logic within router component (meaning keeping the navigation component a pure function of state) like this:

class NavigationRouter extends Component {
  
  componentWillReceiveProps(nextProps) {
    const {appStarting, notLoggedIn} = nextProps
    if (appStarting)
      return

    if (notLoggedIn) {
      Actions.welcomeScreen()
      return
    }
  }

  //do not rerender
  componentShouldUpdate() {
    return false
  }

  render() {
    return (
      <Router>
        <Stack key='root'>
          <Scene initial key='startup' component={StartupScreen} />
          <Scene key='welcomeScreen' component={WelcomeScreen} type='reset' />
          <Scene key='loginScreen' component={LoginScreen} />
          <Scene key='registerScreen' component={RegisterScreen} />
        </Stack>
      </Router>
      )
  }
}

const mapStateToProps = state => ({
  appStarting: state.appStarting,
  notLoggedIn: !state.loggedIn,
})

export default connect(mapStateToProps)(NavigationRouter)

@mnogueron
Copy link

@Matt33 Really like your solution, but it seems to not work when you try to switch between two stacks. Just before moving to the next stack, it pops and try to render the previous page in the stack, thus render things that may not be available anymore (for example in the case of a logout).
I will continue to look for a solution, but it would be awesome to be able to switch between stack as you describe it, in next versions!

@matusholec
Copy link
Author

matusholec commented Feb 5, 2018

@mnogueron Sorry for late response - yes I've came across the same problem. The solution is either not using type='reset' for scenes in another stack but rather using 'replace'.
Also another solution is to use this to navigate between 2 stacks (can be done so just from the components that are included as a component in Scene and therefore are getting navigation in props):

this.props.navigation.navigate('someScreen')

instead of:

Actions.someScreen()

@mnogueron
Copy link

@Matt33 I've ben trying to get your solution working with our product, but I always get stuck at the moment where I need to switch between two stacks. Your solution is working fine when you don't have multiple scenes pushed in the history and that depend on an object from the reducer that can be changed.

I explain my self:

-> LoginStack
-> UserStack
----> Profile (Is displaying content of object X)
--------> Settings

If we consider that architecture (Settings is pushed on top of Profile), and that you can log out from Settings. Logging out will remove content of object X that may be displayed in Profile. Then when you log out in Settings, the app will try to render Profile with data that don't exist anymore before switching to the LoginStack, leading to a crash.
Before, we were switching the Routers for each stack, thus not rendering the empty content on stack change, but now I would have to add a verification process to almost all my components to be sure that the content is not empty...

@matusholec
Copy link
Author

@mnogueron I understand your problem, was dealing with the same one - had to change my app logic cause of that which was fortunately just an easy change for me but believe it might not be for other cases.
Anyway with v4 update quality of this library went rapidly down and while I understand it is still in beta it might be a time to look around for different solutions cause a lot of problems are caused by underlying react-navigation package, which is anything but useful as of now.

@tmaly1980
Copy link

tmaly1980 commented Apr 19, 2018

Is this the best way to approach the v3 equivalent of Switch/selector ?

Basically, in v3 I had:

<Scene
  hideNavBar={true}
  key="root"
  tabs={true}
  title="Main"
  component={connect(state=>({auth:state.auth, app:state.app}))(Switch)}
  selector={props=>!props.app.persisted ? "wait" : props.auth.token ? "loggedIn" : "auth"}
>

Basically, if redux has app.persisted truthy, show 'wait', otherwise show 'loggedIn' if auth.token is set, otherwise default to 'auth'.

The migration does says "Switch is removed - you may use onEnter/onExit handlers for more flexible logic." - but I don't see any examples, not even in the example app.

The confusing part is that onEnter/onExit only seems to work where component is defined in a scene, ie for each subscreen, and not the scene/tab container itself. But it almost seems like onEnter would be an appropriate place to navigate to a specific tab. The problem in my case is that I have multiple levels of scenes deep - there the switch between logged in user and not logged in, among others.

It seems like it's doable your way (via componentWillRecieveProps) but then I have to have several nested if statements to do so to manage the distinct tab systems (ie handling post-login welcome screens or not).

Suggestions?

@aksonov
Copy link
Owner

aksonov commented Aug 8, 2018

onEnter/onExit params are equivalent of Switch (i.e. control of flow). We may restore Switch if it is really needed (feel free to submit PR)

@aksonov aksonov closed this as completed Aug 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants