1

I am creating an app using React Native, Expo and React Navigation, and I don't know the correct method for passing props and functions around.

I have copied the Expo method for navigators that I will build on, but right now I just have App.js calling the following AppNavigator -> MainSwitchNavigator -> HomeScreen

I have then wrapped the main exported App that Expo expects with the Amazon AWS Amplify HOC withAuthenticator. Now I can log in to my app and show hello world securely using Amazon's cognito service.

If I want to log out I can use the signout function bellow that I currently have on the props of App.

class App extends React.Component {

  constructor(props) {
    super(props);
    this.signOut = this.signOut.bind(this);
  }

  signOut() {
    Auth.signOut().then(() => {
      this.props.onStateChange('signedOut', null);
      console.log("signed out");
    }).catch(e => {
      console.log(e);
    });
  }

  render () {
    return (
        <View style={styles.container}>
          <AppNavigator screenProps={{
            signOut: () => {this.signOut}
            }}/>
        </View>
    );
  }
}

export default withAuthenticator(App)

For now I just want to pass this function down to my Home Screen so I can add a button and log out.

AppNavigator

export default createAppContainer(
  createSwitchNavigator({
    // You could add another route here for authentication.
    // Read more at https://reactnavigation.org/docs/en/auth-flow.html
    Main: { screen: MainSwitchNavigator, params: { signOut: this.props.screenProps.signOut } },
  },
  {
    initialRouteName: 'Main'
  })
);

MainSwitchNavigator

export default createSwitchNavigator({
    // Home: { screen: HomeScreen }
    Home: { screen: HomeScreen, params: { signOut: this.props.navigation.params.signOut } }
},{
    initialRouteName: 'Home'
});

HomeScreen

class HomeScreen extends Component {
  render () {
  return (
    <View>
      <Text>Main App Here</Text>
      <Button 
        onPress={this.props.navigation.params.signOut()} 
        title="Sign Out" 
      />
    </View>
  )};
}

export default HomeScreen;

At the moment I get the error

undefined is not an object (evaluating 'this.props.navigation')
<unknown>
MainSwitchNavigator.js
8:62

Which puts its at the point I'm trying to read the props passed in to MainSwitchNavigator.

So my question is, what is good practice on sharing functions and state with screens below the main App and how do I pass the signOut function down to the rest of my components?

=======================================================================

EDIT

I have since worked out how to use screenProps correctly for variables but not for functions. screenProps is passed down through navigators automatically to the screen. So I only have to pass it to the AppNavigator component once and I can access it in HomeScreen. However any functions are not passed down.

E.g for variables, if I modify App.js to pass text to the variable signOut and pass that to screenProps

class App extends React.Component {

  constructor(props) {
    super(props);
    this.signOut = this.signOut.bind(this);
  }

  signOut() {
    Auth.signOut().then(() => {
      this.props.onStateChange('signedOut', null);
      console.log("signed out");
    }).catch(e => {
      console.log(e);
    });
  }

  render () {
    return (
        <View style={styles.container}>
          <AppNavigator screenProps={{signOut: "some text"}}/>
        </View>
    );
  }
}

HomeScreen will then show this as part of the this.props object.

class HomeScreen extends Component {
  render () {
  return (
    <View>
      <Text>Main App Here</Text>
      <Button 
        onPress={console.log(JSON.stringify(this.props))} 
        //  onPress={alert(this.props.screenProps.var3)} 
        title="Sign Out" 
      />
    </View>
  )};
}

export default HomeScreen;

However if I pass a function to AppNavigator instead and do this

<AppNavigator screenProps={{signOut: () => {this.signOut}}}/>

screenProps does not get set and I can't access the function signOut. The only thing I can get to work is this

<AppNavigator screenProps={this.signOut}/>

But what I need is to create a property of screenProps and pass this down. Any thoughts?

Adam
  • 350
  • 1
  • 5
  • 15

2 Answers2

5

passing a function as a screenProp should be

<AppNavigator screenProps={{signOut: this.signOut}} />

then to use do

this.props.screenProps.signOut()
Harry Moreno
  • 10,231
  • 7
  • 64
  • 116
  • Ahh a full day of trying to work out how to access screenProps and I forgot this, thanks for the help. – Adam Jun 26 '19 at 09:15
0

MainSwitchNavigator

export default createSwitchNavigator({
    // Home: { screen: HomeScreen }
    Home: { screen: HomeScreen }
},{
    initialRouteName: 'Home'
});

to send data on navigate

this.props.navigation.navigate('Signout' , { name: 'John Doe', email: 'john@doe.com' })
RiTeSh
  • 513
  • 3
  • 12
  • Thanks Ritesh, unfortunately I don't need to pass data on navigating, I need to pass data as the component mounts. I need screens lower down to have access to a prop on the App component, this is why I am passing the function down. – Adam Jun 25 '19 at 10:21