In my React Native mobile application, I would like to authenticate my users using AWS Amplify Authentication and manage navigation between screens using React Navigation. I tried to implement everything "by the book", i.e. as per the docs and my App conceptually looks like this:
// import statements skipped
class FirstTabScreen extends React.Component {
render = () => {
{/* access some data passed in screenProps */}
const someData = this.props.screenProps.someData;
{/* TODO: how to access AWS Amplify auth data here ?!? */}
{/* this.props contains screenProps and navigation objects */}
console.log("this.props: ", this.props);
{/* this.props.authState is undefined */}
console.log("this.props.authState: ", this.props.authState);
return <View><Text>First Tab - some data: {someData}</Text></View>
};
}
class SecondTabScreen extends React.Component {
render = () => {
const otherData = this.props.screenProps.otherData;
return <View><Text>Second Tab - other data: {otherData}</Text></View>
};
}
const AppNavigator = createBottomTabNavigator({
FirstTab: { screen: FirstTabScreen },
SecondTab: { screen: SecondTabScreen },
});
const AppContainer = createAppContainer(AppNavigator);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
someData: null,
otherData: null,
};
}
componentDidMount = () => {
this.setState({ someData: 'some data', otherData: 'other data' });
};
render = () => {
return (
<Authenticator>
<AppContainer
screenProps={{
someData: this.state.someData,
otherData: this.state.otherData,
}}
/>
</Authenticator>
);
};
}
export default App;
The code sample above skips a few important details; I have created a full Expo Snack.
The AWS Amplify Authenticator
wraps the application and makes authentication data available to embedded components as e.g. this.props.authState
; see docs.
My problem now is that I don't understand how to access this auth data in e.g. FirstTabScreen
: In that component, this.props
contains screenProps
and navigation
objects, but this.props.authState
is undefined
. With AppContainer
and AppNavigator
in between Authenticator
and e.g. FirstTabScreen
, I don't see how to pass auth data as props or transport them by some other means.
Furthermore, I just pass all data to all embedded components in screenProps
; this does work, but not all screens require all data. How would I go about passing them only the data they actually require ? I know about passing parameters to routes, but all the code samples in there have a Button
and use this.props.navigation.navigate(...)
in the onPress
handler. I don't have any buttons on those screens and don't need any - after all, navigating to a different tab is what the tabs in the bottom tab nav bar are for ?!?
Could anyone kindly shed some light on this for me ?
using screeProp
is not encouraged on react navigation v3
I believe it's no longer encouraged to put navigation such as TabNavigator nested within a component. I'm not quite sure how to handle such cases gracefully in V3. I wanted to upgrade from V1 to V3 recently and gave up on the upgrade since I couldn't find enough info on what to do with the nested navigators I have.
another idea from esipavicius
do not wrap screens to stack navigator which include the tab navigator. Maybe you Can use
navigationAction
setState
orsetParams
withparams
andnavigator
key. When You Candispatch
it. And automaticaly changes state with params to which scren you dispatched.
OPTION 1 - USING SCREEN PROP
Your option of passing screen props
class App extends React.Component {
componentDidMount = () => {
this.setState({ someData: 'some data', otherData: 'other data' });
};
render = () => {
return (
<Authenticator>
<AppContainer
screenProps={{ myProp: "test" }}
/>
</Authenticator>
);
};
}
OPTION 2 - USING THE REACT CONTEXT API
Using the Context api and wrapping it around your <AppContainer>
.
class App extends React.Component {
render = () => {
return (
<FormContext.Provider value={this.state}>
<AppContainer />
</FormContext.Provider>
);
};
}
Then consume it in each one of your screens.
class FirstTabScreen extends React.Component {
render(){
return(
<FormContext.Consumer>
{ (context) => (
// your logic
)}
</FormContext.Consumer>
};
}
Option 3 - TAB NAVIGATOR OBJECT
You can pass a TabNavigatorObject
createBottomTabNavigator(RouteConfigs, BottomTabNavigatorConfig);
You can pass an extra option to the BottomTabNavigator
in the BottomTabNavigatorConfig
.
const BottomTabNavigator = StackNavigator(routesConfig, BottomTabNavigatorConfig)
The BottomTabNavigatorConfig is an option and you can check the api docs
{ initialRouteName: 'Home', navigationOptions: .... }
Option 4 - RETRIEVE PARAM FROM PARENT
as explained on github and in the api docs, you can retrieve the parent component and then use getParam
.
const parent = navigation.dangerouslyGetParent();
const yourParam = parent.getParam('yourParamName', 'default value')
You can find many discussion about passing props with tab navigator on github