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

Params not being passed when navigating between Stacks (repro included) #143

Closed
davideweaver opened this issue Feb 1, 2017 · 52 comments
Closed

Comments

@davideweaver
Copy link

I use the following code to create an initial screen that does not have a NavBar but pushes on a stack that contains a floating NavBar. When navigating to the second stack, params are being passed. It doesn't appear that those params are being passed to the new screen. This can be seen in the attached screenshot. undefined should not be showing.

image

index.ios.js

import React, { Component } from 'react';
import { StackNavigator } from "react-navigation"
import { AppRegistry, Text, View, Button } from 'react-native';

class FirstView extends Component {
  render() {
    const {navigate} = this.props.navigation;
    return (
      <View style={{marginTop:20}}>
        <Text>First View</Text>
          <Button title="Second" onPress={() => navigate("HeaderStack", {name:"Jo"})} />
      </View>
    );
  }
}

class SecondView extends Component {
  render() {
    const {state} = this.props.navigation;
    var name = state.params ? state.params.name : "<undefined>";
    return (
      <View><Text>Second View: {name}</Text></View>
    );
  }
}

const HeaderStack = StackNavigator({
  SecondView: { screen: SecondView }
}, {
    headerMode: "float",
});

const NoHeaderStack = StackNavigator({
  FirstView: { screen: FirstView },
  HeaderStack: { screen: HeaderStack }
}, {
  headerMode: "none"
});

AppRegistry.registerComponent('ParamsRepo', () => NoHeaderStack);

Is this a bug, or is there another way to accomplish the same thing?

@elliottjro
Copy link

I think you're running into this problem because your SecondView is nested in a StackNavigator so state params aren't being passed into the children.

Have you tried replacing HeaderStack with SecondView when defining NoHeaderStack? ie:

const NoHeaderStack = StackNavigator({
  FirstView: { screen: FirstView },
  HeaderStack: { screen: SecondView }
}, {
  headerMode: "none"
});

@davideweaver
Copy link
Author

davideweaver commented Feb 1, 2017

@elliottjro What you suggest does work correctly. That doesn't accomplish the UX I'm looking for though. Is what I'm asking for something I shouldn't expect to work?

@elliottjro
Copy link

@davideweaver hmm so what you need to do is somehow pass the params from HeaderStack = StackNavigator() to SecondView extends Component() which should be possible.. Just cant find where I read it before

@elliottjro
Copy link

hey @davideweaver try this:

<Button title="Second" onPress={() => navigate("HeaderStack",{},
  {
    type: "Navigate",
    routeName: "SecondView",
    params: {name:"Jo"}
  }
)} />

Whats going on is -> we navigate to "HeaderStack" and then set a second "Navigate" action to be handled by the child router (which is the "HeaderStack" Navigator). reference

@satya164
Copy link
Member

satya164 commented Feb 1, 2017

It's expected behaviour right now, but I agree that's it's confusing. cc @ericvicenti @skevy

@davideweaver
Copy link
Author

davideweaver commented Feb 2, 2017

@elliottjro Thanks for the code. Now that I see it the docs make sense. For those following along, you would use the same pattern for navigating to a deeper screen as well. Here's the code if HeaderStack was placed into a TabNavigator...

navigate("FirstTab", {}, {
    type: "Navigate", 
    routeName: "HeaderStack",
    action: {
        type: "Navigate", 
        routeName: "SecondView", 
        params: {name: "Jo"}
    }
});

That being said, the fact that navigate is smart enough to get to the correct screen without defining the additional actions, I think it would be great to have it pass the parameters through automatically too (unless I'm missing something).

@ilaif
Copy link

ilaif commented Feb 6, 2017

I still can't get this working.

I have a Drawer inside a Stack, and I'm trying to navigate between two screens in the Drawer.
Tried all combinations, how would you call navigate from Notifications to Main with params in the following setup:

const MainNavigator = DrawerNavigator({
    Main: {screen: Main},
    Notifications: {screen: Notifications},
});

const SplashStackNavigator = StackNavigator({
    Login: {screen: Login},
    MainNavigator: {screen: MainNavigator}
}, {
    initialRouterName: 'Login',
    headerMode: 'none',
});

I've tried:

this.props.navigation.navigate('Main', {param: 'param'});
this.props.navigation.navigate('MainNavigator', {}, {
    type: "Navigate",
    routeName: "Main",
    params: {param: 'param'},
});

And in all cases inside Main constructor:

const navParams = this.props.navigation.state.params;

is undefined.

Thanks.

@davideweaver
Copy link
Author

@ilaif it looks like you're calling the same way I have it working. I haven't tried it with a Drawer Navigator though. That may behave differently than a TabsNavigator. The only other thing I see different, which shouldn't matter, is that I'm using destructuring when accessing params:

const { state } = this.props.navigation;
var title = state.params.session.title;

Maybe try dumping your navigation when the target page loads:

componentDidMount() {
  console.log("NAV: ", this.props.navigation)
}

@jamesisaac
Copy link

jamesisaac commented Feb 7, 2017

This workaround doesn't really make sense to me (nor can I even get it to work).

To take an example which I imagine would be quite a common use case: say the main navigator of my app is a StackNavigator, with the main screen being a ListView of contacts. When I click a contact, I want to navigate to a screen with sliding tabs (TabNavigator), where each tab displays a different piece of info about that contact.

With this nested navigate action workaround, the best I can hope for is that I can pass the contactId param to just one of the tabs. (Which would cause an error on the other tabs unless lazyLoad: true). Perhaps I could dispatch the SetParams action on each of the tabs, but that seems quite convoluted.

What I think would make a lot more sense here is if I could simply call naviagion.navigate('ContactTabNavigator', { contactId: id }), and have that param automatically passed down to all the nested screens.

@JulianKingman
Copy link

JulianKingman commented Mar 9, 2017

I have the latest version and am still having this problem.

Root navigator:

const AppNavigator = StackNavigator({
  MainDrawer: {screen: MainDrawer},
}, {
  navigationOptions: {}
});

MainDrawer:

const MainDrawer = DrawerNavigator({
  Home: {screen: Home},
  PastIncidents: {screen: PastIncidents},
  NewIncident: {screen: NewIncident},
}, {
  navigationOptions: {...},
  drawerPosition: 'right',
  contentOptions: {
    style: {
    },
  },
});

on Home:

navigation.navigate('NewIncident', {incidentId});

The incidentId is not passed to the NewIncident component. Ideas?

This is what the props look like:
screen shot 2017-03-09 at 8 34 54 am

@can-cc
Copy link

can-cc commented Apr 6, 2017

i have same problem

@nonameolsson
Copy link

I experience the same issue.

In my container:

  onPress(data) {
    this.props.navigation.navigate('NewsItem', { news: data })
  }

I am sure that data is a valid object. It has worked before, and during debugging.

And in my NewsItem screen:

....
  render() {
    const { navigate } = this.props.navigation;
    const { title, date, text, image } = this.props.navigation.state.params.news

Here is the result. The state only contains keyand routeName.
screen shot 2017-04-06 at 15 30 57

@JulianKingman
Copy link

Looks like this may get fixed by this PR: #969

@grabbou
Copy link

grabbou commented Apr 26, 2017

Yeah, looks like this issue was a duplicate of the ones mentioned by the PR. That should be fixed in latest master.

@grabbou grabbou closed this as completed Apr 26, 2017
@satya164
Copy link
Member

The linked issues seem different.

@grabbou
Copy link

grabbou commented Apr 26, 2017

You are right. I got confused by one of the commits that talks about Drawer #143 (comment). Drawer should work fine. The original issue is about navigating between Stacks.

@grabbou grabbou reopened this Apr 26, 2017
@TGNC
Copy link

TGNC commented May 4, 2017

The issues seems to be fixed for TabNavigator in ^1.0.0-beta.9.
However, this only work for NavigationActions.navigate() request.
Trying to make the same navigation but with NavigationActions.reset instead - did not worked

For example:

const goToTab = NavigationActions.navigate({
            routeName: 'Tab',
            params: {
                user: 'bla'
            }
        });

const reset = NavigationActions.reset({
            index: 0,
            actions: [
                goToTab
            ]
        });

Running this.props.navigation.dispatch(goToTab); did send the user inside the params,
However running this.props.navigation.dispatch(reset); left the params empty.

I dig a little bit in the code and found what seem to be the problem -
It look like because the navigation was from outside the TabNavigation (The TabNavigation is inside a StackNavigation) when running the regular navigate request than the request get type of NavigationActions.INIT, but when you run it from inside a reset request it get type of NavigationActions.NAVIGATE.
And in TabRouter.js for NavigationActions.INIT request type the code that set the params to the children screens is being executed. However, the code for NavigationActions.NAVIGATE request type is the following:

didNavigate = !!order.find((tabId: string, i: number) => {
          if (tabId === navigateAction.routeName) {
            activeTabIndex = i;
            return true;
          }
          return false;
        });
        if (didNavigate) {

which return false - because tabId (which is the name of the TabNavigation itself is not matching any one of it's child screen names), and because it return false the code that set the params to the children is not executed.

I don't know how to solve it, so if anyone can fix it please do.
I found a fix for now - in the reset navigation, calling the action init instead of navigate fix the problem

@naomipol
Copy link

naomipol commented Jul 4, 2017

The workaround mentioned above does not fit my navigation structure as well.

I have a main DrawerNavigator where each route is a StackNavigator. The StackNavigator has an initial route configured so when I route to the stack I get that screen.

const DrawerNav = DrawerNavigator({
  Home: { screen: HomeScreen, },
  Document: { screen: DocumentStack}},
  {
    initialRouteName: 'Home'
  });
const DocumentStack = StackNavigator({
    List: { screen: ListScreen },
    Details: { screen: DetailsScreen }
  },{
    initialRouteName: 'List',
  });

When navigate to the stack and do an additional navigation to that (initial) screen, I get the screen with a back button - and when I press it, it goes back to the exact screen but without the parameter.

navigate('Document', {},
      {
        type: "Navigate",
        routeName: "List",
        params: {list: 'orders'}
      })

Screen shot after navigation: (The list page is second in navigation stack and has a back button)

image

Screen shot after I press the back button: (The list name is empty - because no params)

image

Is this going to be fixed
Is there any other solution in the mean time?

@jordanmkoncz
Copy link

jordanmkoncz commented Jul 6, 2017

@TGNC I'm having the same problem as you. I've created a separate issue since this one has been closed and appears to be for a different problem. See #2073.

@dmr07
Copy link

dmr07 commented Jul 8, 2017

+1. Also getting undefined params when navigating from one stack to a screen in a nested stack.
The solution mentioned earlier in the thread does not work for me:

this.props.navigation.navigate('parentStack', {}, {
      type: "Navigate",
      routeName: "childStack",
      params: payload
    });

param still undefined.

Would be great if navigators passed params along.

@smitthakkar1
Copy link

Any Solution to this ? Facing the same problem

@yukkeechang
Copy link

I have this issue too +1

@smitthakkar1
Copy link

This might help and work for me.

On Screen1 I am passing the data while navigating to Screen2 like this:

<TouchableHighlight
onPress = {() => { this.props.navigation.navigate('CandidateProfile', user = {i}) }} />

On Screen 2, I am receiving this data which is "i" here,

const {i} = this.props.navigation.state.params;

I have constructor on both component and which is like this :

constructor(props){
super(props);
this.state = {};
}
Hope helps you guys. This took so much time.

@yukkeechang
Copy link

Thing is, I'm passing this param from one screen and trying to display it a few screens away in my stack nav and accessing the param value there gives me an undefined :(

@yasir-netlinks
Copy link

yasir-netlinks commented Nov 26, 2017

Hi , I am facing the same issue not being able to pass Params.
Top Level Stack

const routeConfiguration = {
    Login: { screen: Login },
    Home: { screen: HomeWrapper,
        navigationOptions: {
            header: null
        }    
    },
    UserInfo: { screen: UserFormsTabBar, 
        navigationOptions: {
            header: null
        } 
    },
    Profile: { screen: ProfilePage },
}

I am trying to navigate from Profile to UserInfo. Now UserInfo is a Tab Navigator. However, each Tab in this navigator is in turn in its own Stack Navigator (which I believe the reason for not being able to see Params).
So when I log in FirstTab there are no Params passed. Can someone help ? Please see this for more clarification: https://stackoverflow.com/questions/47494531/react-native-unable-to-pass-params-using-react-navigation?noredirect=1#comment81944512_47494531

@spencercarli spencercarli added bug and removed question labels Nov 28, 2017
@spencercarli spencercarli reopened this Nov 28, 2017
@nonameolsson
Copy link

Thanks for opening this one again @spencercarli !

@tinashe96
Copy link

Is there a solution to this? Tried all suggestions from above but nothing works. Im trying to pass props between two screens under DrawerNavigation.

@zeroFruit
Copy link

zeroFruit commented Dec 8, 2017

I logged every navigation state (everytime I navigate to other screen) and made my own react-navigation helper functions.

// MyComponent.js
const renderHeader = (params) => <Header params={ params } />;

class MyComponent extends Component {
  static navigationOptions = {
    header: ({ navigation }) => renderHeaderWithNavigation(navigation)(renderHeader)
  }

  componentDidMount() {
    const params = { ... };
    setParamsToNavigation(this.props, params);
  }
}
// Router.js
export const navigateTo = (props, to, params = {}) => {
  props.navigation.navigate(to, params);
};

export const setParamsToNavigation = (props, params) => {
  props.navigation.setParams({ ...params });
};

export const renderHeaderWithNavigation = (navigation) => {
  const params = getParamsFromNavigationState(navigation.state);
  
  return (renderHeaderMethod) => {
    return renderHeaderMethod(params);
  };
};

// recursive function for nested navigator
export const getParamsFromNavigationState = (state) => {
  if (hasPath(state, 'index')) {
    const { index, routes } = state;
    return getParamsFromNavigationState(routes[index]);
  } else {
    return hasPath(state, 'params') ? state.params : null;
  }
};

And if I want to pass params to other screen as props, I used HOC

export const mapNavigateParamsToProps = (WrappedComponent) => {
  return class MappedComponent extends Component {
    static navigationOptions = WrappedComponent.navigationOptions;

    render() {
      const { navigation } = this.props;
      const { state: { params } } = navigation;
      return <WrappedComponent { ...this.props } { ...params } />;
    }
  };
};

@kkkelicheng
Copy link

I have a similar problem
My views structure like this

const MainView = TabNavigator(
    {
        Home: {
            screen:HomePage        
        },
        Tab2: {
            screen:Tab2     
        },
        Tab3:{
            screen:Tab2        
        }
    },
    configs
}

const navRoutes = {
    Main: {
        screen:MainView           
    },
    Other1:{
		screen:Other1
    },
    Other2:{
		screen:Other2
    },
}

export const MainNavigator =
    StackNavigator(navRoutes,configs)
}

Now in page Other2(from Home navigate Other1 and navigate Other2).

const setParamsAction = NavigationActions.setParams({
            params: {title:"haha"},
            key: homePagekey
        });
        this.props.navigation.dispatch(
            setParamsAction
        );

When I goBack Home, I can not get params from navigation。
I guess tabNavigator and stackNavigator params is not related。
How can resolve this problem?

@Dror-Bar
Copy link

Dror-Bar commented Jan 10, 2018

I would like to share a similar problem where I couldn't pass params to my DrawerNavigator, as well as my solution. This is a specific case but it might help somebody.

Before

Structure:

const MyStack = StackNavigator({
    Login: {screen: Login},
    Home: {screen: Home}
    // more screens...
});
export const MyDrawer = DrawerNavigator({
    Main: { screen: MyStack }
},
    { contentComponent: MyMenu }
);

In Home Screen:

openMenu() {
      this.props.navigation.navigate('DrawerOpen', userName);
 }

In MyMenu:

render() {
   const userName = this.props.navigation.state.params;
   return (
       <View>
              <Text>{userName}</Text>
              // more stuff
       </View>
    );
}

this.props.navigation.state.params is undefined.

I figured that you can only send navigation props when navigating to a new screen - 'DrawerOpen' does not count for this. To solve this I had to change my structure a little bit, instead of having the DrawerNavigator as the root drawer, I have a StackNavigator as the root drawer.

After

const MyStack = StackNavigator({
  Home: {screen: Home},
  // more screens...
});

const MyDrawer = DrawerNavigator({
  Main: { screen: MyStack }
},
  { contentComponent: MyMenu }
);

export const RootStack = StackNavigator({
  Login: {screen: Login},
  Drawer: {
     screen: MyDrawer,
     navigationOptions: { header: null } } //Prevent double header
});

Now in Login screen I navigate to Drawer like this:

_login() {
    this.props.navigation.navigate('Drawer', { userName: userName });
}

And in MyMenu (which is part of 'Drawer'):

render() {
  const { userName } = this.props.navigation.state.params;
  return (
    <View>
          <Text>{userName}</Text>
          // more stuff
    </View>
  );
}

It works!

@Diwei-Chen
Copy link

We have a similar setting as #143 (comment)

What we found is the latest version 1.0.0-beta.27 doesn't work, but version 1.0.0-beta.21 works for us.

@naomipol
Copy link

I would like to share a work around for the problem I mentioned in my comment above, maybe it will help somebody:

// my stack navigator
const DocumentStack = StackNavigator({
    List: { screen: ListScreen },
    Details: { screen: DetailsScreen }
  },{
    initialRouteName: 'List',
  });

// create a wrapper component for stack navigator
// and pass params and rootNavigation via screenProps
// (solution!!!)
class DocumentsStackNav extends Component {
 render() {
  const { navigation } = this.props;
  return <DocumentStack 
	   screenProps={{
	        params: navigation.state.params,
	        rootNavigation: navigation
	  }}/>
}

// my drawer navigator - were 'Document' references the wrapper component
const MenuDrawer = DrawerNavigator({
  Home: { screen: HomeScreen },
  Document: { screen: DocumentsStackNav },
},
{
  initialRouteName: 'Home',
})

// now we can simply navigate to DocumentsStack passing params
navigation.navigate('Document', params)

// and access them in the ListScreen (initialRoute of DocumentStack)!!!!
// we also have access to the DrawerNavigator's navigation 
// if we would like to navigate to a different drawer item from here...
class ListScreen extends Component {
  render() {
    const { params, rootNavigation } = this.props.screenProps;
  }
}

This solution works great for me! No need for extra navigation action, and the most important, access params!!!

@brentvatne
Copy link
Member

I copied the exact code from the initial post into a Snack and it works as expected: https://snack.expo.io/HyhE2T38G

I imagine this has been resolved, please create a new issue and follow the updated issue template if you're still having problems! :)

@wellyshen
Copy link

wellyshen commented Feb 11, 2018

@bartolkaruza I wrap my three stackNavigators in a TabNavigator. Then I pass a param from one of the stackNavigator through navigation.navigate('DestinationRouteName', { ... }). I can't get the passed param from the DestinationRouteName stackNavigator. This issue still exist. Now my workaround is to use redux for passing the param instead.

react-navigation: v1.0.0
react-native: v0.53.0
node: v9

@bartolkaruza
Copy link

@wellyshen I'm sure you meant to tag someone else :)

@carlos-proogresa
Copy link

Something very similar happens to me like @wellyshen, my provisional solution is to work with version react-navigation@1.0.0-beta.21

@ghost
Copy link

ghost commented Feb 14, 2018

did the beta version allow you to pass params between nested navigators using state.props? @carlos-proogresa

having trouble with this issue right now as well

@wellyshen
Copy link

See I tell u guys!

@carlos-proogresa
Copy link

@sethnkruse, I feel my English because I'm using a translator. I hope I'm answering your question.

I am currently passing parameters from one screen to another with the version I have discussed.

A piece of code from my router.js file:

const HomeStack = StackNavigator({
  Home: { 
    screen: HomeContainer,
    navigationOptions: ({ navigation }) => ({  
      headerStyle: {
        backgroundColor: (navigation.state.params.section.backgroundColor),
      },
      headerTitleStyle: {
        color: 'white',
      },
      headerTintColor: 'white',      
    }),
  }, 

  
...


const HomeTabs = TabNavigator({
  HomeStack: { screen: HomeStack },
  ChatStack: { screen: ChatStack },
  ProfileStack: { screen: ProfileStack },
  NotificationStack: { screen: NotificationStack },
}, {
  navigationOptions: { 
    gesturesEnabled: false,
  },
  swipeEnabled: false, // TODO: Cambiar a true
  backBehavior: true,
  tabBarOptions: {
    showLabel: false,
  },
  initialRouteName: 'NotificationStack',
  headerMode: 'none',
});

export const RootStack = StackNavigator({
  Splash: { 
    screen: SplashContainer,
    navigationOptions: { header: null },
  },
  LoginStack: { screen: LoginStack },
  SectionStack: { screen: SectionStack },
  HomeTabs: { screen: HomeTabs },
}, {
  transitionConfig: () => ({
      screenInterpolator: (props) => (fade(props))
  }),
  headerMode: 'none',
  initialRouteName: 'Splash',
});

It all starts in RootStack and from Splash I navigate to HomeTabs by passing parameters. HomeTabs is a TabNavigator that contains HomeStack and finally Home is my screen where I consume these parameters.

From SplashScreen the code I use to navigate and send parameters:

const resetAction = NavigationActions.reset({
  index: 0,
  key: 'HomeTabs',
  actions: [ 
    NavigationActions.navigate({ routeName: 'HomeTabs', params: { section } })
  ]
});

navigation.dispatch(resetAction);

In HomeScreen I use them with:

static navigationOptions = ({ navigation }) => ({    
    tabBarIcon: ({ focused, tintColor }) => (
      <MaterialIcon 
        name='store'
        size={ 25 }
        color={ focused ? navigation.state.params.section.backgroundColor : tintColor }
      />
    ),
});

And that's all, I hope it's understood.

@dihan
Copy link

dihan commented Mar 11, 2018

the issue I found with @carlos-proogresa method is that if you are using TabNavigator the screen does not go to that specific tab/stack but just another screen in its own stack.

@animaonline
Copy link

animaonline commented Mar 16, 2018

Still getting this when navigating from one StackNavigator to another, this is easily reproducible, so why is has the issue been closed?

@montao
Copy link

montao commented Mar 18, 2018

My params were not being passed and the state object did not contain the params object even though I set it. I tried everything. It was not working. Then I changed my StackNavigator and put my screen in the right StackNavigator and it works.

import React, { Component } from 'react';
import { View, Text, StyleSheet } from 'react-native';

import { StackNavigator } from 'react-navigation';
import { Icon } from 'react-native-elements';

import ListsHome from '../views/lists_home';
import ListsDetails from '../views/lists_detail';
import Fonts2 from '../views/fonts_home';

const ListsTabView = ({ navigation }) => (
  <ListsHome banner="Lists" navigation={navigation} />
);

const ListsDetailTabView = ({ navigation }) => (
  <ListsDetails banner="Lists Detail" navigation={navigation} />
);

const ListsTab = StackNavigator({

  Home: {
    screen: ListsTabView,
    path: '/',
    navigationOptions: ({ navigation }) => ({
      title: 'Ads',
      headerLeft: (
        <Icon
          name="menu"
          size={30}
          type="entypo"
          style={{ paddingLeft: 10 }}
          onPress={() => navigation.navigate('DrawerOpen')}
        />
      ),
    }),
  },
  Lists_Detail: {
    screen: ListsDetailTabView,
    path: 'lists_detail',
    navigationOptions: {
      title: 'Lists Detail',
    },
  },   
    Details: { screen: Fonts2 },navigationOptions: ({ navigation }) => ({
        title: 'Ads',
        headerLeft: (
            <Icon
                name="menu"
                size={30}
                type="entypo"
                style={{ paddingLeft: 10 }}
                onPress={() => navigation.navigate('DrawerOpen')}
            />
        ),
    })
});

export default ListsTab;

@aaron25mt
Copy link

Has any found a solution to this issue? Have tried every solution in this thread and nothing's working for me.

@SoldierCorp
Copy link

More than a year and still not fixed, wow!

@brentvatne
Copy link
Member

brentvatne commented Apr 19, 2018

please create a new issue and follow the issue template, this one is old and before the current maintainers started working on it. i would be happy to look into this if you can do that, thanks!

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

No branches or pull requests