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

Set initialRouteName and initialRouteParams based on props #458

Closed
dasmikko opened this issue Feb 23, 2017 · 73 comments
Closed

Set initialRouteName and initialRouteParams based on props #458

dasmikko opened this issue Feb 23, 2017 · 73 comments
Assignees

Comments

@dasmikko
Copy link

Hi

Is it possible to have a conditional homescreen?

Like, the app knows from the start that your not logged in, then automatically show the login screen, else show the "real" home.

Hope my question makes sense.

@grabbou
Copy link

grabbou commented Feb 23, 2017

Hey,

Assuming your static knowledge is:

const isLoggedIn = true;

you can just use initialRouteName property and pass it to StackNavigation to select the route.

Otherwise, you can:

  1. switch screen in render()
  2. switch navigator in render()

@satya164
Copy link
Member

You can't set initialRouteName and initialRouteParams based on props right now, but I think we need this.

@satya164 satya164 changed the title Conditional homescreen? Set initialRouteName and initialRouteParams based on props Feb 23, 2017
@grabbou
Copy link

grabbou commented Feb 23, 2017

Yeah, sounds like a valid use case. I'll take a look into that in the morning (I need it anyways for that exact purpose)

@dasmikko
Copy link
Author

dasmikko commented Feb 23, 2017

@grabbou
you can just use initialRouteName property and pass it to StackNavigation to select the route.

I'm not sure how to do this. I'm pretty new to ReactJS and React Native.

My StackNavigator is a constant, so I can't change any variables in the config for it.

@grabbou
Copy link

grabbou commented Feb 23, 2017 via email

@dasmikko
Copy link
Author

Now I understand.

Looking forward for the new feature! :)

@grabbou grabbou self-assigned this Feb 23, 2017
@BenMcH
Copy link

BenMcH commented Feb 27, 2017

@jeffreymendez1993 That would add to the stack and cause the back button on android for example to take you to the "InitialScene" view. The gestures are still there.

Using the reset action should be all that is needed.

https://reactnavigation.org/docs/navigators/navigation-prop

@dasmikko
Copy link
Author

I'm a little consfused for when I should fire the reset.

Because I have a login screen, but I don't want to user to be able to go back.

@BenMcH
Copy link

BenMcH commented Feb 27, 2017

@dasmikko I made a method in my splash screen that takes the name of the route as an argument and I can just call that with the route name.

I can provide an example in a couple hours when I get home.

@dasmikko
Copy link
Author

Looking forward to see the example.

@grabbou
Copy link

grabbou commented Feb 28, 2017

Alternatively, you can have two navigators and if statement in render. Both use-cases are valid. I think for time-being this is a satisfactory solution and the issue can be closed.

@BenMcH
Copy link

BenMcH commented Feb 28, 2017

@dasmikko I completely forgot last night.

What I have is something like this in my splash screen class:

reset = (route) => {
  let resetAction = NavigationActions.reset({
    index: 0,
    actions: [
      NavigationActions.navigate({ routeName: route})
    ]
  });
  this.props.navigation.dispatch(resetAction);
}

I have to say though that I believe that what @grabbou suggested might be a better solution.

@satya164
Copy link
Member

satya164 commented Mar 1, 2017

@grabbou

Params during render is a valid usecase. Even if @dasmikko's very specific use case is solved, it doesn't address the main issue. I changed the title so we can track this feature, because it's pretty important.

@satya164 satya164 reopened this Mar 1, 2017
@grabbou
Copy link

grabbou commented Mar 1, 2017

I feel it would also solve a couple of other issues we have spotted, esp. with Tab navigator.

@satya164
Copy link
Member

satya164 commented Mar 1, 2017

I think we could deprecate screenProps too once we have functionality to pass initialParams during render.

@MrHubble
Copy link

@grabbou

Alternatively, you can have two navigators and if statement in render

Are you able to provide sample code on how to do this? I tried following the example given by @jeffreymendez1993 but I get an error as it's my initial screen and there is no value for const { navigate } = this.props.navigation;

@f0rr0
Copy link

f0rr0 commented Mar 25, 2017

Any progress on this?

@woowalker
Copy link

is this feature request try to figure out the problem that react-navigation lib cant receive the props from native ? my issue is that, in hybrid App i try to receive the props that deliver from native App, but i cant receive any props.
if so, is any progress on this ?
if not, how can i receive the props deliver from native App.
thanks.

@shrutic
Copy link

shrutic commented May 22, 2017

Is there a fix in works for this long-standing feature request? I have scenario where:

  • The first screen (initialRouteName) depends on props ( I have taken the route of creating 2 navigators with different initialRoute)

  • If one of the 2 navigators is selected, I need to pass a dynamic initialRouteParams, that is residing in the this.props of the component launching the navigator

Appreciate insight on how to address the second bullet point. Let me know if I can provide any additional information that can help with the creation of this feature

@WeIio
Copy link

WeIio commented May 24, 2017

@sata
Pass initalRouteName as a props still not work now,so I have to define two or more StackNavigators as an alternative solution for conditional choice.
Thanks for your job anyways

@vitorebatista
Copy link

Any progress on this?

@jaredmharris
Copy link

Need this!

@jasan-s
Copy link

jasan-s commented Jun 14, 2017

I naively thought I could do this :(

{ initialRouteName: (props) => { return props.screenProps.isAuthed ? 'Home' : 'Auth' } }

Get the error , Cannot get config because the routes does not have a routeName

@dmr07
Copy link

dmr07 commented Jul 10, 2017

@grabbou Regarding your comment of using if statements in the render function, I must disagree that it is satisfactory. By using conditional rendering, screen transitions are lost, which results in UI ambiguity for the user. I have attempted to wrap the elements in AnimatedAPI, but transitions are noticeably choppy for some reason.

It seems to me that if StackNavigators are designed to be nested, they ought to have ways to dynamically set the initial route or else it becomes self-defeating.

Would love to see this implemented when you guys have a chance.

@stantoncbradley
Copy link

@richardgirges are you using redux to manage your nav state?

@philsam
Copy link

philsam commented Oct 2, 2017

@stantoncbradley - Thank you so much for taking the time to create a working example. I'm digging into your gist. I'll update you on my findings soon.

@richardgirges
Copy link

@stantoncbradley I haven't integrated redux with react navigation - but my app is making heavy use of redux anyway so I wouldn't mind doing so if it would help resolve this issue.

@stantoncbradley
Copy link

we haven't had to nest routers yet, but we've found moving our nav state to redux to be easier to work with since it abstracts the nav state out of the navigator. Redux should be able to persist your the nested nav states regardless what is being rendered

@philsam
Copy link

philsam commented Oct 8, 2017

@stantoncbradley - i've been busy but i took time to dig into the gist and test it in a real scenario. Your solution actually works pretty well. The only thing i noticed was in InitialRouteExample/Navigation.js
The following are not necessary for it to work in the routeConfigs object.

login: { screen: LoginScreen },
main: { screen: MainScreen },

Apart from that, its a great solution and i highly recommend others to follow the pattern.

@kelset
Copy link

kelset commented Oct 8, 2017

Pinging OP @dasmikko to know if this issue can be closed, since the solution proposed by @stantoncbradley seems to be working fine for multiple users.

Maybe @stantoncbradley can you do a small PR to add your example or an explanation to the docs? 😇

@richardgirges
Copy link

@kelset I think that @stantoncbradley's solution is clearly the best approach but I would appreciate this issue staying open until there's a more official solution - one that can work out of the box for both redux and non-redux integrated react-navigation apps. In addition to that, it feels like a lot of overhead that one must take on just to handle dynamic initial routes (including the need to re-define any components that can be initial routes in a second place, after already defining them as routes in your navigators).

Obviously this can fall in priority now that there's a "good enough" solution - it's definitely good enough for me to keep going. But I fear that closing issues when there are only workarounds in place is what has ultimately depleted the quality of react-navigation. I say that a concerned citizen who has placed his bets on the future of react-navigation, it's not meant to be taken as criticism.

If people disagree, then close away. Perhaps I'm misunderstanding the goal behind react-navigation's issue-triaging strategies.

@kelset
Copy link

kelset commented Oct 8, 2017

Hey @richardgirges thanks for your reply; I'll keep this issue open, but as you can see in #2585 probably it won't be implemented for v1.0. That said we are always trying to review each and every PR so it's not like if somebody provides a PR with this feature we will not merge it ;)

@richardgirges
Copy link

Thanks @kelset! I have noticed that the open PRs have been slowly depleting. I may even take a crack at this one myself sometime soon, just started getting familiar with the react navigation codebase :)

@dasmikko
Copy link
Author

dasmikko commented Oct 8, 2017

@kelset This can be closed, it works for me too. 👍

@kelset
Copy link

kelset commented Oct 8, 2017

I appreciate your attitude @richardgirges :) How about starting with a new issue that follows the issue template for the feature request you have been writing about? After that you can open a PR and start working on it :)

Don't worry about "not being too familiar" with the codebase, or even deadlines, it's all about you also learning while trying :)

I'll use myself as an example ;) Look here: react-native-community/directory#50

(and I'll close this since OP agreed on it)

@kelset kelset closed this as completed Oct 8, 2017
@amorenew
Copy link

My Full solution
#2683

@Ariel08081214
Copy link

how to solve this issue without redux??

@amorenew
Copy link

amorenew commented Oct 13, 2017

@danna88 I don't use redux in my solution

@Ariel08081214
Copy link

@amorenew Thank you. I see you are using the InitialScreen, used to jump to other pages, I want to do the function is automatically Login, can I use Login page as InitialScreen? In addition, I used the react-native-splash-screen, but I encountered a problem: I do autoLogin in componentWillMount, then I closed Splash in componentDidMount, but it always render LoginScreen first,then navigate to other pages。

@amorenew
Copy link

amorenew commented Oct 14, 2017

@danna88 I use splash without a library just with timer,
As you can see in initial screen

render() {
        return null;
    }

which renders nothing so the required screen only will be visible to the user
in componentWillMount
I use GeneralPref.getInitialScreen() which is an AsyncStorage.getItem('initailscreen')
so by AsyncStorage you can make your logic and checks to navigate to your required screens

@Ariel08081214
Copy link

@amorenew thank you very much! I 'll try it !

@j-mendez
Copy link

I use a splash screen and set the initial stack to render. I set up the configuration of my app during the initial launch and when it is completed the router/Stack is returned.

@iamrutvik
Copy link

So basically redirecting user to the Home screen in nested navigator scenario, I did this little trick. As react-navigation does not provide a way to set the initialRouteName, we dispatch an action to navigate to the drawer stack i.e. Home screen.

Here is our Navigation file with Redux integration, which will show you the nesting of various navigators

// drawer stack
const DrawerStack = StackNavigator({
  DashboardScreen: { screen: DashboardScreen, navigationOptions: { title: 'Event', drawerLabel: 'Event' } },
  MyAccountScreen: { screen: MyAccountScreen, navigationOptions: { title: 'My Account', drawerLabel: 'My Account' } },
}, {
    headerMode: 'screen',
    navigationOptions: ({ navigation }) => ({
      headerStyle: {
        backgroundColor: Colors.darkBlue,
        shadowOpacity: 0,
        shadowOffset: { height: 0, },
        elevation: 0,
      },
      headerTitleStyle: {
        color: Colors.snow,
        alignSelf: 'center',
      },
      headerLeft: navigation.state.routeName == 'DashboardScreen' ? <TouchableOpacity activeOpacity={0.2} style={styles.leftIcon} onPress={() => {
        navigation.dispatch(NavigationActions.navigate({ routeName: 'DrawerOpen' }));
      }}><Image large source={Images.Bar} style={styles.bar} /></TouchableOpacity> : <TouchableOpacity activeOpacity={0.2} style={styles.leftIcon}
        onPress={() => {
          if (navigation.state.params && navigation.state.params.onBackPress) {
            navigation.state.params.onBackPress();
          }
          navigation.goBack();
        }}><Icon name="angle-left" size={28} color="snow" /></TouchableOpacity>,
      headerRight: <TouchableOpacity activeOpacity={0.2} style={styles.leftIcon} onPress={() => {
        navigation.dispatch(NavigationActions.navigate({ routeName: 'DrawerOpen' }));
      }}><Icon size={28} color="snow" /></TouchableOpacity>

    })
  })
const DrawerNavigation = DrawerNavigator({
    DrawerStack: { screen: DrawerStack }

}, {
    contentComponent: DrawerComponent,
    drawerWidth: 300,
  })
// login stack
const LoginStack = StackNavigator({
  LoginScreen: { screen: LoginScreen },
  RegisterScreen: { screen: RegisterScreen },
  ForgotPasswordScreen: { screen: ForgotPasswordScreen },
  CountryListScreen: { screen: CountryListScreen },
  VerificationScreen: { screen: VerificationScreen },
}, {
    headerMode: 'none',
  })
// Manifest of possible screens
const PrimaryNav = StackNavigator({
    drawerStack: { screen: DrawerNavigation },
    loginStack: { screen: LoginStack },
  }, {
    // Default config for all screens
    headerMode: 'none',
    initialRouteName:  'loginStack',
    transitionConfig: noTransitionConfig,
})
export default PrimaryNav

Now we have done a little bit tweak in the first App.js file which will run just after botting the React native application from index.ios.js/index.android.js/index.js file. We just set a state loading to check whether data is loaded from AsyncStorage or not. And if the data is loaded, then we will return our Root component else we will return null, which will not show login screen or anything on the UI front.

import '../Config'
import DebugConfig from '../Config/DebugConfig'
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import RootContainer from './RootContainer'
import createStore from '../Redux'
import { AsyncStorage, Linking } from 'react-native'
import Constants from '../Components/Constants'
import LinkRoutes from '../Config/LinkRoutes';
import {NavigationActions} from 'react-navigation';

// create our store
const store = createStore()

/**
 * Provides an entry point into our application.  Both index.ios.js and index.android.js
 * call this component first.
 *
 * We create our Redux store here, put it into a provider and then bring in our
 * RootContainer.
 *
 * We separate like this to play nice with React Native's hot reloading.
 */
class App extends Component {

  constructor(props) {
    super(props)
    this.state = {
      loading: false,
    }
  }


  componentWillMount() {
    AsyncStorage.getItem(Constants.KEY_IS_LOGIN, (err, result) => {
      this.setState({ loading: true} , () => {
        if (result === '1') {
          const navigateAction = NavigationActions.navigate({
            routeName: 'drawerStack',
            index: 0,
          });
          store.dispatch(navigateAction);
        }
      })
    });
  }


  render() {
    if(this.state.loading){
      return (
        <Provider store={store}>
          <RootContainer />
        </Provider>
      )
    }else{
      return null
    }
  }
}

// allow reactotron overlay for fast design in dev mode
export default DebugConfig.useReactotron
  ? console.tron.overlay(App)
  : App

In componentWillMount I have just dispatched a NavigationActions that will navigate to the Drawer screen/Home screen. Hope this helps.

@abs-cbn-iptv-xcms
Copy link

abs-cbn-iptv-xcms commented Jan 8, 2018

easy as pie
-------App.js------
render() {
var signedIn = false ; // however , whatever
var routes = {
Categories: { screen: Categories },
Landing: { screen: Landing }
}

if (signedIn) { 
  routes = {
    Landing: { screen: Landing },
    Categories: { screen: Categories }
  }
}

routes.SignIn = { screen: SignIn }

var MyApp = StackNavigator(
  routes );

return <MyApp />;

@stantoncbradley
Copy link

@eugenecp this will only evaluate 'signedIn' once, right? How does this get updated once the user signs in/out? Wouldn't it have to rebuild the whole stackNavigator?

@dwivediamit
Copy link

Hi, I am using react-native tab navigator for two tabs, account and profile i want if user is logged in then only it will show the account and profile screen otherwise it will go to login screen.

Here I have given onPress condition but how to check user is logged or not in below code...
onPress={() => {
if (props.navigationState.index === false) {
alert('Hi');
props.navigation.navigate("Profile")
} else {
alert('Yes');
props.navigation.navigate("Login")
}
}}

This is my code index.js

import React, { Component,AsyncStorage } from "react";
import Home from "./Home.js";
import Schedule from "./Schedule.js";
import Account from "./Account.js";
import Location from "./Location.js";
import Profile from "./Profile.js";
import { TabNavigator } from "react-navigation";
import {
Button,
Text,
Icon,
Item,
Footer,
FooterTab,
Label
} from "native-base";

import styles from "../../css/style";

export default (MainScreenNavigator = TabNavigator(

{
Home: { screen: Home},
Schedule: { screen: Schedule },
Account: { screen: Account },
Location: { screen: Location },
Profile: { screen: Profile }
},
{
tabBarPosition: "bottom",
tabBarComponent: props => {
return (

    <Footer>
      <FooterTab>
        <Button
          vertical
          active={props.navigationState.index === 1}
          onPress={() => props.navigation.navigate("Schedule")}
        >
          <Icon name="calendar" />
          <Text>Schedule</Text>
        </Button>

       

        <Button
          vertical
          active={props.navigationState.index === 2}
          onPress={() => {
            if (props.navigationState.index === false) {
              alert('Hi');
              props.navigation.navigate("Account")
            } else {
              alert('Yes');
              props.navigation.navigate("Login")
            }
          }}>
          <Icon name="barcode" />
          <Text>Account</Text>
        </Button>

      
        
        <Button
          vertical
          active={props.navigationState.index === 3}
          onPress={() => props.navigation.navigate("Location")}
        >
          <Icon name="ios-navigate-outline" />
          <Text>Location</Text>
        </Button>


        
        <Button
          vertical
          active={props.navigationState.index === 4}
          onPress={() => {
            if (props.navigationState.index === false) {
              alert('Hi');
              props.navigation.navigate("Profile")
            } else {
              alert('Yes');
              props.navigation.navigate("Login")
            }
          }}>
          <Icon name="person" />
          <Text>Profile</Text>
        </Button>
   
      
      </FooterTab>
    </Footer>
  );
}

}
));

@slorber
Copy link
Member

slorber commented Jan 30, 2018

A workaround for that is to wrap the navigation into a class, get a ref on it, and dispatch a reset action on mount.

class WrappedNavigation extends Component {

  componentDidMount() {
    this.dispatch(NavigationActions.reset({
      index: 0,
      actions: [
        NavigationActions.navigate({routeName: this.props.initialRouteName})
      ]
    }));
  }

  dispatch = (...args) => this.myNav.dispatch(...args);

  return <MyNav ref={c => this.myNav = c}/>;
}

Your static initial screen could render nothing (() => false) and it will be automatically replaced by the correct screen on mount.

@kkotkkio
Copy link

kkotkkio commented Feb 28, 2018

const RootStackNavigatorWrap = (initialRouteName) => {

  const RootStackNavigator = StackNavigator(
    {
      Main: {
        screen: MainTabNavigator,
      },
      Join: {
        screen: JoinNavigator
      },
    },
    {
      ...initialRouteName,
      navigationOptions: ({ navigation }) => ({
        header: null
      }),
    }
  );
  return <RootStackNavigator/>;
};

export class RootNavigator extends React.Component {
  render() {
    return <RootStackNavigatorWrap initialRouteName={this.props.app.initialRouteName}/>;
  }
}

@wellyshen
Copy link

wellyshen commented Feb 28, 2018

If I want to keep the transition of react-navigation with dynamic initialRouteName any suggestion for it?

@amorenew
Copy link

@wellyshen may help #2683

@wellyshen
Copy link

wellyshen commented Mar 1, 2018

@amorenew Thank you my solution is not to invoke navigator re-rendered immediately so that the transition of react-navigation won't be cut off. BTW this post explaining the idea of login flow of react-navigation. Help it'll help someone else.

@brentvatne
Copy link
Member

if you need to set the initialRouteName as a prop it is because there is some data that you need to fetch asynchronously before you render the app navigation. another way to handle this is to use a switchnavigator and have a screen that you show when you are fetching the async data, then navigate to the appropriate initial route with params when necessary. see https://reactnavigation.org/docs/auth-flow.html for a full example of this.

that said, if you think that it is important to support these options as props then please feel free to thumbs up this rfc issue: react-navigation/rfcs#23. and if you really care about it a lot, please open a proper rfc from the rfc template! thanks!

@react-navigation react-navigation locked and limited conversation to collaborators Mar 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests