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

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.

Related

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

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.

React Navigation passing function as parameter

so i have lot of screen that have similar function eg:
ForgotPasswordCaptcha
ForgotUserIDCaptcha
RegisterCaptcha
these screens have same layout and functionality except they have different "Next" Button function in ForgotPassword users will be redirected into loginscreen, in ForgotUserID the user will be redirected into page that shows his userID. and in Register it will redirect user to Succes Page.
.
so the function that i create in navigator is handleOkay, and when i press the button in SomeReusableScreen it gives me this warning
Non-serializable values were found in the navigation state. Check:
forgotUserIDStackScreen > FUinputCASAScreen > params.handleOkay (Function)
This can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params.
what i want to achieve is i to pass function as parameter in Stack navigator so the screen will just invoke that function to redirect/doing something with redux,
i dont know i should ignore this warning since in other screens it will handle redux sagas, set asyncstorage
so i have found the way to do what i wanted.
instead of passing the screen as component in Stack.Screen i pass it as child like this
<Stack.Screen
initialParams={{progress: 0.2}}
name={routes.nameofthisroute}
options={({navigation, route}) => ({
headerTitle: props => {
let currentProps = {navigation, route};
return <NavigationHeaderTwo {...currentProps} />;
},
...otherStyle,
})}>
{props => (
<PickMethodScreen handlePickMethod={handlePickMethod} {...props} />
)}
</Stack.Screen>
and i props some function and state from Stack Navigator and its written in this docs here
we can also create react context so component can consume it without having lot of props
I implemented a very simple example to clear the concept. You can pass function as a prop and can dispatch up to the parent component using the same props key. This way you can utilize reusable component.
//consider this your generic Component
function IButton(props){
//receive the method as props
//dispatch the method back to the parent
return (
<button onClick={()=>props.handleClick('hello')}>click me </button>
)
}
const App=()=>{
function handleOnClick(text){
alert(text);
}
return(
<div>
{/*pass the function as Prop*/}
<IButton handleClick={handleOnClick}/>
</div>
)
}
ReactDOM.render( < App / > , document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

React Navigation Custom Header and Custom Back Button

I am using React Navigation. I created a custom header component myself. I call this component into other components. But the back button I use on the header doesn't work for other components. How can I add this to the react navigation stack.
navigation.back() or navigation.navigate('Home)
I get an error when I write.
Maybe you not transmit navigation? print console.log(navigation). And you can use navigation.goBack()
You can use this: function LeftMenu(props) { return ( <Drawer.Navigator initialRouteName={props.route} drawerContent={(props) => <Custom_Side_Menu {...props}/>} drawerContentOptions={{activeTintColor: 'rgb(219,218, 218)'}}> <Drawer.Screen name="Map" component={MapScreen}/> </Drawer.Navigator> ); }
And you can get navigation in props LeftMenu

React this.props.navigation.openDrawer() in child component?

I have an openDrawer method via React Navigation 2 working great in my View. However, when I create a child component and place my openDrawer button in it, I can no longer access this.props.navigation. How do I pass that down into the child component? I looked at state but that doesn't seem to be the correct way to address this? I am new to React/Native. Here is my button inside the main view and it works fine this way.
<View style={styles.menuBar}>
<TouchableOpacity style={styles.menuButton}
onPress={() => this.props.navigation.openDrawer()}>
<Text>☰</Text>
</TouchableOpacity>
// Other Stuff inside menuBar.
</View>
This menu button has a few other items as well and I am wanting to group together in a class component as a child that I can just import into various screens.
import { topMenuBar } from '../components/menuBar';
<topMenuBar />
However, the menu button no longer works now. I know it's because this.props is now referring to class topMenuBar and not the original class which is part of the nav structure. But I don't know the proper procedure for this step, whether it's using state or calling NavigationActions from react-navigation in the child method somehow.
Thanks for the help!
Every component opened using react-navigation has "this.props.navigation".
If you need it in child components you should pass to them as a prop:
export default class Home extends Component{
render(){
<View>
<OtherComponent navigation = {this.props.navigation}/>
</View>
}
}
Note: if you want a better help you should always provide code creating a fiddle and organize better your answers..

How to pass navigator reference to nested child components?

I want to make navigation drawer list item clickable, in that scenario i want navigator object to navigate another scene. How can i pass navigator reference there from main component to child component? My child components of Main components are ListView and ListItems. Any suggestion will be helpful.
You are able to pass it down just like any other prop. I'm assuming you're passing navigator down to the routed component...
<Navigator
ref='Navigator'
renderScene={(route, navigator) => {
let RoutedComponent = route.component
return (
<RoutedComponent navigator={navigator} {...route.props}/>
)
}}
/>
If so, in the RoutedComponent just pass the navigator down to it's child components through props.
<MyChildComponent navigator={this.props.navigator}/>