React Native - pass props from One screen to another screen (using tab navigator to navigate)
Asked Answered
F

4

3

I need to pass data from my HomeScreen to my SecondScreen. There are a ton of examples of how to do this if i'm clicking a button on the HomeScreen to navigate to SecondScreen, but can't find anything showing how to pass to SecondScreen if I'm using a v2 bottom tab navigator to go from HomeScreen to SecondScreen. I've tried screenprops and a couple other methods and spent about 8 hours trying to figure it out but could not get it to work. Any idea how to do this? Please, any hint would be amazing. Here is my code:

MainTabNavigator.js:

 const config = Platform.select({
  web: { headerMode: 'screen' },
  default: {},
});


const HomeStack = createStackNavigator(
  {
    Home: HomeScreen,

  },
  config

);

HomeStack.navigationOptions = {
  tabBarLabel: 'Home',
  tabBarIcon: ({ focused }) => (
    <MaterialIcons name="home" size={32}  />
  ),
};

HomeStack.path = '';


const SecondStack= createStackNavigator(
  {
    Second: SecondScreen,
  },
  config
);    

SecondStack.navigationOptions = {
  tabBarLabel: 'Second screen stuff',

  tabBarIcon: ({ focused }) => (
    <MaterialIcons name="SecondScreenIcon" size={32}  />
  ),
};

SecondStack.path = '';


const tabNavigator = createBottomTabNavigator({
  HomeStack,
  SecondScreen
});

tabNavigator.path = '';

export default tabNavigator;

HomeScreen.js:

  class HomeScreen extends Component {

  constructor(props){
    super(props);  
  }

 componentDidMount(){

   this.setState({DataFromHomeScreen: 'my data that Im trying to send to SecondScreen'})

  }

//....

SecondScreen.js:

class SecondScreen extends Component {

      constructor(props){
        super(props);  
      }


   render()
   return(     

         <View>{this.props.DataFromHomeScreen}</View>

    )


    //....

****Please find THINGS I'VE TRIED below:****

HomeScreen.js: when i do this it receives it at first but then passes null

render(){

return(
<View>
 //all of my home screen jsx
<SecondScreen screenProps={{DataFromHomeScreen : 'data im trying to pass'}}/>
</View>
)
}

MaintTabNavigator.js: when i do this it receives it at first but then passes null

HomeStack.navigationOptions = {
  tabBarLabel: 'Home',
  tabBarIcon: ({ focused }) => (
    <MaterialIcons name="home" size={32}  />
  ),
};

<HomeStack screenProps={{DataFromHomeScreen:'data im trying to pass'}}/>


HomeStack.path = '';

I've tried like 5 other ways too that I can't even remember at this point. I don't want to have to call my database again in the second screen to get user info. Nobody I know knows react or react native. The React Native documentation at https://reactnavigation.org/docs/en/stack-navigator.html is minimal at best, only showing the below:

const SomeStack = createStackNavigator({
  // config
});

<SomeStack
  screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>

even if you go to the examples in the documentation and search for the word 'screenprop' you will not see any mention of the screen prop feature in either of the examples. All questions that I've seen only address how to pass props on button click which is easy. Is what I'm trying to do possible? I'm sure I'm not the only person using tab navigator who's retrieved data in the homescreen and need to pass it to other screens . Any advice helps. thanks.

ps. Here is my Sign in class that is calling the Home screen:

class SignInScreen extends React.Component {
static navigationOptions = {
  title: 'Please sign in',
};


render() {
  return (


    <View
    style={styles.container}
    contentContainerStyle={styles.contentContainer}>

    <View>
    <SocialIcon
    title='Continue With Facebook'
    button
    type='facebook'
    iconSize="36"
    onPress={this._signInAsync} 
    />
    </View>

  );
}


_signInAsync = async () => {

    let redirectUrl = AuthSession.getRedirectUrl();
    let result = await AuthSession.startAsync({
      authUrl:
        `https://www.facebook.com/v2.8/dialog/oauth?response_type=token` +
        `&client_id=${FB_APP_ID}` +
        `&redirect_uri=${encodeURIComponent(redirectUrl)}`,
    });      

    var token = result.params.access_token
    await AsyncStorage.setItem('userToken', token);

    await fetch(`https://graph.facebook.com/me?fields=email,name&access_token=${token}`).then((response) => response.json()).then((json) => {

          this.props.navigation.navigate('Home',
          {
              UserName : json.name,
              FBID : json.id,
              email : json.email

          });     


    }) .catch(() => {
       console.log('ERROR GETTING DATA FROM FACEBOOK')
      });

};
 }

export default SignInScreen;
Flower answered 24/7, 2019 at 15:20 Comment(2)
Are you trying to pass the data when tapping on the bottom tab button for SecondScreen, or through a button in HomeScreen?Eject
I'm fetching user data in the homescreen from my database. Ideally i would send it to SecondScreen to render stuff as soon as i get the data from the fetch function in the homescreen. I could settle for sending the data when i click on the SecondScreen bottom tab button though if the first option is not possible.Flower
F
0

I ended up using Redux, it only took me like 100 read throughs and attempts to learn it, but once I learned it it's amazing and simple.

Flower answered 27/12, 2019 at 0:44 Comment(1)
If this will solve the use case, can you provide the author with an example?Divergent
O
2

I think you're calling your database in componentDidMount in your HomeScreen component, (I'm right?) and because another component in the same hierarchy needs the same data, you should considerer wrapping this into a new component and do the call to your data in that father component, then you pass the data to all the children that needs it. This is the react way to do things. The state of HomeScreen should not have the data, your data should live in a parent component in a higher hierarchy and pass the data to children as props.

In this way when you create your tabs you can pass the props as the react native docs suggest:

import { createBottomTabNavigator, BottomTabBar } from 'react-navigation-tabs';

const TabBarComponent = (props) => (<BottomTabBar {...props} />);

const TabScreens = createBottomTabNavigator(
{
  tabBarComponent: props =>
    <TabBarComponent
      {...props}
      style={{ borderTopColor: '#605F60' }}
    />,
 },
);

Another solution could be to use a global state management with Redux or something similar.

I hope this helps.

Edit:

class Home extends React.Component{
 constructor(props){
   super(props);
   this.state = {data: null}
 }
 componentDidMount() {
   //get your props from navigation (your facebook credentials)
   //your call to database
   this.setState({data: yourResponseData});
 }
 render(){
   const TabNavigator = createBottomTabNavigator(
     {
       HomeScreen: props =>
         <HomeScreenStack
           {...this.state.data}
         />,
       SecondStack: props =>
         <SecondStack
           {...this.state.data}
         />,
      },
     );
   return(
     <TabNavigator />
   )
 }
}

const App = createAppContainer(Home);

export default App;
Osis answered 24/7, 2019 at 17:55 Comment(6)
I'm using react-navigation v2 so I do not have react-navigation-tabs. I'm calling the Home screen from a SignInScreen which is where i get FB authentication info, once i get to HomeScreen i am calling my database with that FB authentication info from SigninScreen. I see what you are saying about needing to use a higher order component but I'm not sure how to implement that given my structure.Flower
Luis, I added my SignIn class at the bottom of my question if that helpsFlower
I edited my answer to add a Home component that implements this structure.Osis
i tried your edited answer and get this errror in homescreen: 'invariant violation: the navigation prop is missing for this navigator. In react-navigation 3 you must set up your app container directly. by the way, I just put the <TabNavigator/> part inthe middle of my actual Views etc in the homescreen.Flower
where do I put <TabNavigator/> relative to the rest of my homescreen jsx?Flower
In that case with React Navigation 3 you need an App Container, take a look at my last edit. this code should be at the end of your file, first you need to create your HomeScreenStack component and your SecondStack, that part of your code stays without changes it should work as you posted it, use your HomeStack and SecondStack as is in the createBottomTabNavigator function. Be aware in this modules that at the first render the data will be null.Osis
E
1

Use this.props.navigation.navigate.

In your HomeScreen, once you have the data you want to send, then navigate over to SecondScreen like so:

this.props.navigation.navigate('Second', { data: yourData })

To access this in SecondScreen whenever it is navigated to using navigation props, you can use NavigationEvents along with this.props.navigation.getParam.

/* your imports */
import { NavigationEvents } from 'react-navigation';

export default class SecondScreen extends React.Component {
  /* your methods and properties */
  render() {
    <View>
      <NavigationEvents
        onDidFocus={() => this.setState({ data: this.props.navigation.getParam('data', {}) })}
      />
      { /* your SecondScreen render code */ }
    </View>
  }
}

Edit: For example, with your SignInScreen implementation, to access the props, use:

const username = this.props.navigation.getParam('UserName', '')
const fbid = this.props.navigation.getParam('FBID', 0)
const email = this.props.navigation.getParam('email', '')
Eject answered 24/7, 2019 at 18:20 Comment(4)
Will calling this.props.navigation.navigate take me to the second screen when I call it? I don’t want to be taken to the second screen unless I click on bottom tab navigator buttonFlower
In that case, I would recommend saving the data in AsyncStorage and retrieving it on componentDidMount in the second screenEject
It will be a 2d array and need to access it from multiple screens. this is just a simplified exampleFlower
Redux would be the best way to go for that route, but AsyncStorage could still work for that use case. To prevent having to rewrite getting it on componentDidMount every time, you could write a HOC (reactjs.org/docs/higher-order-components.html) that will take in the target component, get the data from AsyncStorage on mount, and then pass the data as normal props to the target component.Eject
D
1

This is the basic approach that I am using:

import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';

const TestComponent = (props) => {
  return <Text>{`TestComponent: ${props.name}`}</Text>;
};

const Home = () => {
  const Tab = createBottomTabNavigator();

  return (
    <View style={{flex: 1}}>
      <Tab.Navigator>
        <Tab.Screen name="Screen 1">
          {() => <TestComponent name="test 1" />}
        </Tab.Screen>
        <Tab.Screen name="Screen 2">
          {() => <TestComponent name="test 2" />}
        </Tab.Screen>
      </Tab.Navigator>
    </View>
  );
};

Notice that to pass the props to a Screen I am using a child function instead of passing a value to component. The child function can then return the component you want in the syntax you are used to, that has the props available. In this case, the props are the simple name, but you can expand this to handle your state.

Divergent answered 9/4, 2020 at 1:58 Comment(0)
F
0

I ended up using Redux, it only took me like 100 read throughs and attempts to learn it, but once I learned it it's amazing and simple.

Flower answered 27/12, 2019 at 0:44 Comment(1)
If this will solve the use case, can you provide the author with an example?Divergent

© 2022 - 2024 — McMap. All rights reserved.