In React Native app passing navigation to class component puts it in another navigation object - react-native

In my React Native app, when I pass navigation to a class component, it puts it in another navigation object and now I have to add one more navigation to call the navigate() method.
So, instead of this.props.navigation.navigate("somewhere"),
I have to use this.props.navigation.navigation.navigate("somewhere")
Here's how I pass navigation from a functional to a class component:
const ParentComponent = ({ navigation }) => {
return(
<View>
<ChildComponent navigation={navigation} />
</View>
);
}
export default ParentComponent;
And in the child component which happens to be a class component with access to Redux store, I recieve navigation like this:
class ChildComponent extends Component {
render() {
return(
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigation.navigate("Route123")}>
<Text>Go to Route 123</Text>
</TouchableOpacity>
</View>
);
}
}
function mapStateToProps(state, navigation) {
myAccount: state.account.myAccount,
navigation: navigation
}
export default connect(mapStateToProps)(ChildComponent);
Notice that in the ChildComponent, I had to add one more navigation to access the navigate function i.e. this.props.navigation.navigation.navigate("Route123");
I think I'm making a fairly fundamental mistake somewhere but not sure where.
The version of React Navigation in my app is as follows and the React Native version is 0.66.3:
"#react-navigation/drawer": "^6.1.8",
"#react-navigation/material-bottom-tabs": "^6.0.9",
"#react-navigation/native": "^6.0.6",
"#react-navigation/native-stack": "^6.2.5",

As #Hagai Harari stated, you should not put navigation as the second parameter of mapStateToProps function. As described here, second parameter of mapStateProps takes ownProps which corresponds to the component props.
You can use withNavigation. But when I see your dependencies, you are using react-navigation#^6 which does not have HOC called as withNavigation.
You can choose to use this.props.navigation after removing unnecessary navigation param from mapStateToProps.
What I would suggest is to use functional component instead of class components, then you will be able to use useNavigation which react-navigation#^6 exposes. In the end, you will obtain navigation object without repetition of passing it down to children components.

Related

Why useNavigation hook doesn't work in custom side bar?

If I create custom drawer:
<DrawerStack.Navigator
drawerContent={props => <SideMenu {...props} />}>
It requires to pass props in order to get navigation object inside it.
And if I have following:
<ClientsStack.Navigator>
<ClientsStack.Screen name="Clients" component={ClientsScreen} />
<ClientsStack.Screen
name="ClientDetails"
component={ClientDetailsScreen}
/>
</ClientsStack.Navigator>
And inside ClientsScreen I have FlatList which has:
renderItem={({ item }) => <ClientCard id={item.id} />}
inside ClientCard component which is not screen it's just dummy component
I can use useNavigation hook there.
Why it can be used there but not in custom drawer component?
You can't use useNavigation inside your drawerContent cause the hook is only available from your screens and their childs.
At the origin the only way to get navigation was to pass it from your screen to their childs. But since the useContext hook exist, the library provides a useNavigation to easily get the navigation in deep nested components. (and simplify the code a lot)
A drawer custom component is not a screen or wrapped into a screen. So there is just no reason to get the navigation from it with useNavigation. And I easily guess there is no usecase where we have deep nested components inside it cause it is usually just a menu.

Splash screen with PersistGate

I have below code base
Navigator.js
export default createAppContainer(createSwitchNavigator(
{
// screendesign: screendesign,
SplashScreen: SplashScreen,
App: drNav,
AuthStack: AuthStack
},
{
initialRouteName: 'SplashScreen',
}
));
import SplashScreen from './screens/SplashScreen'
<Provider store={store}>
<PersistGate loading={<SplashScreen/>} persistor={persistor}>
<View style={styles.container}>
<Navigator />
</View>
</PersistGate>
</Provider>
splashScreen.js
componentDidMount() {
if (this.state.isAuthenticated) {
this.props.navigation.navigate("App");
}
else {
this.props.navigation.navigate("AuthStack");
}
}
I am getting below error
Before implementing redux, I have used this page as entry screen for
my app, showing as splash screen and behind checking if user is
authenticated or not, if it is authenticated redirecting to login page
else dashboard. It was working fine, but now I have used redux, then
the scenario is working similarly only diff this screen is not visible
since redux persis load data from storage
Please help I am new in react native unable to understand this error
Thanks
The navigation prop is available to all components defined inside the navigator.
The SplashScreen component is not part of your Navigator so it doesn't have access to the navigation prop.
But I don't think you need it there.
Your SplashScreen component is a dump component that will be shown to the user for as long as the PersistGate needs to load the stored data to your redux store.
So when the loading of the data is completed you will see the rest of the components as they are defined inside the PersistGate. So I don't see why you would need to use the navigation from this loading component.
In case you do really need to access the navigation prop from the SplashScreen you can follow this guide: https://reactnavigation.org/docs/en/connecting-navigation-prop.html
Looks like your code unable to find navigation in SplashScreen component.
1) Try to console that in SplashScreen component's render method.
2) Also check for redux configuration, there may be case that issue is with your redux configuration because of that your component unable to access navigation props.

How to Create Drawer menu without Navigation?

I am looking to create a drawer, similar to a drawer navigator, but without the routes/navigation requirement.
I plan on placing some other components there that update a query. Any recommendations? Specifically the drawer would be used to display picklists, sliders, and date range components that would update the state and variables used in updating markers rendered on a map shown on the home page.
With Redux
You can use the contentComponent of the createDrawerNavigator to create your own custom drawer and bind it to redux-store.
By dispatching the actions with relevant queries you can update the variables as they are passed from the store to your Component.
Without Redux
You can either create a CustomDrawer component with similar animation and render it in your Component or use this react-native-drawer.
import Drawer from 'react-native-drawer'
class Application extends Component {
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
render () {
return (
<Drawer
ref={(ref) => this._drawer = ref}
content={<DrawerContentComponent {...// Your Updater props}/>}
>
<Component />
</Drawer>
)
}
})

props automaticaly given as parameter

I would like to use a custom component and I need it to have the navigation props in order to navigate inside this component.
For now I'm passing navigation as a classic prop:
<MyVideo videoSource={require('../Image/S01E01DB.mp4')}
thumbnailSource={require('../Image/S01E01DB.mp4')}
navigation={this.props.navigation}/>
But is it possible to always have him set as it was given as a prop?
I need that in order to simplify the usage of my prop.
As said in the comments section by #jonrsharpe, the navigation props is only automatically given to routes declared with createStackNavigator or the others react-navigationnavigators`
There's a tool you can use from react-navigation to connect any component to your parent navigator:
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
In case of nested navigators, the withNavigation function will get the closest parent of your component.
You will find more informations on this link

can't navigate to different screen using react-native-drawer with react-native-router-flux

I'm using react-native-router-flux for the Navigation system and using react-native-drawer for the sidebar. If an user clicks on menu item which is in the sidebar, I want to redirect user to different screen and close the Drawer. I'm using the following code snippets.
Actions.refresh({key: 'drawer', open: false}) to Close the Drawer
Actions.pageTwo.call() to Open the Second Page
Both are working without any problem if I'm using it in separate functions. But, If I trigger both the snippets from the same function. It is not working.
Thanks in Advance,
I have this scenario working for me with both libraries. At a high level I create a custom component to render the content of react-native-drawer which I pass a function to responsible for closing the drawer. Then when I press one of the drawer items I fire both a react-native-router-flux navigation action (in my case a PUSH) and I call the function passed in to close the drawer.
Here is what defining my drawer looks like. Remember DrawerNavigationConten is ultimately just a ListView or whatever implementation you prefer to render the drawer content.
class RootComponent extends Component {
...
closeDrawer() {
this.drawerRef.closeDrawer();
}
render() {
const drawerNavigationContent = (
<DrawerNavigationContent
closeDrawer={this.closeDrawer.bind(this)}
/>
);
return <Drawer
ref={(ref) => { this.drawer = ref; }}
content={drawerNavigationContent}
...
>
...here I define my react-native-router-flux scenes...
</Drawer>
);
}
...
}
Here are the important items in DrawerNavigationContent
import { Actions } from 'react-native-router-flux';
class DrawerNavigationContent extends Component {
...
navigate(location) {
Actions[location]();
this.props.closeDrawer();
}
...
render() {
...
<TouchableOpacity onPress={this.navigate(...some dynamic scene key...)}>
...
</TouchableOpacity>
...
}
}
I've worked with react-native-router-flux and had some issues with the drawer as well. If both scenes are children of the Drawer like so
<Scene key="navigationDrawer" component={NavigationDrawer}>
<Scene key="pageOne" component={PageOne}/>
<Scene key="pageTwo" component={PageTwo}/>
</Scene>
you can use Actions.pageTwo({key: 'navigationDrawer', open: false}) which should close the drawer upon navigation.
Otherwise you could use Actions.refresh({key: 'navigationDrawer', open: false) on PageTwo's componentWillMount or componentDidMount methods.