React Native component onPress navigation Button Native-Base - react-native

I am developing an android app with React Native and Native-Base.
In my component button i can't get onpress to work to change page.
import React from "react";
import { StyleSheet, View, TouchableOpacity } from "react-native";
import { NativeBaseProvider, Button, Text } from "native-base";
const ButtonAPP = (props) => {
//const linkBottom = props.linkBottom;
const textBottom = props.textBottom;
return (
<NativeBaseProvider>
<View style={styles.viewButtonAPP}>
<Button
shadow={2}
style={styles.buttonAPP}
onPress={() => props.navigation.navigate("Home")}
>
<Text style={styles.textButton}>{textBottom}</Text>
</Button>
</View>
</NativeBaseProvider>
);
};
const styles = StyleSheet.create({
styles....
});
export default ButtonAPP;
THe error is:
navigate is not defined
I don't understand why for pages in my app it works but for a component like the button it doesn't

The navigation object will be passed as props by the navigation framework only to components that are defined as screens inside a navigator. If this is not the case, then the navigation object will not be passed to this component.
You have three choices.
1) Use the useNavigation hook
const ButtonAPP = (props) => {
const navigation = useNavigation();
...
<Button
shadow={2}
style={styles.buttonAPP}
onPress={() => navigation.navigate("Home")}
>
}
2) Pass the navigation object as a prop from a parent that is a screen defined in a navigator
Assume that ScreenA is a screen that is defined in a navigator, e.g. a Stack. This would look like the following snippet.
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="ScreenA" component={ScreenA} />
</Stack.Navigator>
);
}
Assume that ButtonApp is used as a child in ScreenA. Then, we can pass the prop manually, since the framework will not do this.
const ScreenA = (props) => {
return <ButtonApp navigation={props.navigation} />
}
const ButtonAPP = (props) => {
...
<Button
shadow={2}
style={styles.buttonAPP}
onPress={() => props.navigation.navigate("Home")}
>
}
3) Let ButtonApp be a screen inside a navigator
This choice usually does only make sense for actual views in the application and not for individual components. However, this is still an option.
Using the same example as in 2). However, this will work with any navigator.
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="ButtonApp" component={ButtonAPP} />
</Stack.Navigator>
);
}
Accessing the navigation object via props as you have tried initially works in this case as well.

Related

React native navigation prop not being passed to the function

React native navigation from stack navigator is not being passed down to the Restaurantsscreen function
This is the function which has to use naviagtion but it cant
export const RestaurantsScreen = ({navigation}) => (
<SafeArea>
<View style={{"backgroundColor": "rgba(255, 255, 255, 255)"}}>
<FlatList
data={[
{ name: 1 }
]}
renderItem={(item) =>{return (
<Pressable onPress={ ()=>console.log(navigation)}>
<RestaurantInfoCard />
</Pressable>
);
}}
and these are the functions where stack navigator is defined
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={Screentwo} /> // this screen is currently loaded and the screentwo is importing the Restaurants screen function inside it as a component
</Stack.Navigator>
</NavigationContainer>
);
}
Kindly help
thanks
You have two solutions :
Past the navigation props from the Screentwo to you're child component RestaurantsScreen
Used the useNavigation hooks directly in your RestaurantsScreen component (see link for this hook : https://reactnavigation.org/docs/use-navigation/)
Example of the first solution:
//Screentwo inherit navigation props direclty, as this component is called in your Stack.Navigator
export const Screentwo = ({navigation}) => {
return(
//As RestaurantsScreen is not called directly in your Stack.Navigator,
//this component will not inherit navigation props,
//you need to past manually navigation props from Screentwo in this way
<RestaurantsScreen navigation={navigation} />
)
}
Example of the second solution with useNavigation :
//Directly in your RestaurantsScreen component
import { useNavigation } from '#react-navigation/native';
//No need to import navigation props
export const RestaurantsScreen = () => {
//Call useNavigation, and navigation can be used in the exact same way as if you past it as a props
const navigation = useNavigation()
return (
<SafeArea>
the rest of your code here
</SafeArea>
)
}

React navigation v5 slow because rerenders current screen before opening drawer

I'm working on a bigger app and have some lag when opening the drawer. It takes about 1 second for the drawer animation to begin.
I looked into it with react profiler and saw that the drawer is rendered and the current screen is rerendered before the drawer opens. This makes things feeling slow, I would not suspect the current screen to rerender.
Here is how my pseudo navigation stack looks topdown:
//Toplevel
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const RootStack = createStackNavigator();
<NavigationContainer ref={navRef} theme={navTheme} linking={linking}>
<RootStack.Navigator>
{loggedIn ? (
<RootStack.Screen component={inBetweenComponent} /> // => calls drawer
) : (
// auth screen
)}
</RootStack.Navigator>
</NavigationContainer>
const DrawerNavigator = () => {
const Drawer = createDrawerNavigator();
const route = useRoute();
const activeRoute = getFocusedRouteNameFromRoute(route);
return (
<Drawer.Navigator
drawerContent={props => <DrawerContent {...props} activeRoute={activeRoute} />}
>
<Drawer.Screen name="Notifications" component={NotificationsNavigator} />
<Drawer.Screen name="Conversations" component={ConversationsNavigator} />
</Drawer.Navigator>
}
const DrawerContent = ({ activeRoute }) => {
const navigation = useNavigation();
return (
<DrawerContentScrollView>
<TouchableNative onPress={goToNotifications} >..some text...</TouchableNative>
<TouchableNative onPress={goToConversations} >..some text...</TouchableNative>
</DrawerContentScrollView>
)
}
const NotificationsNavigator = ({ navigation }) => {
const Stack = createStackNavigator();
return (
<Stack.Navigator
screenOptions={{
headerLeft: () => <HeaderLeft navigation={navigation} />, // -> contains open drawer button
}}
initialRouteName="Notifications"
>
<Stack.Screen
name="Notifications"
component={NotificationsScreen}
/>
</Stack.Navigator>
);
}
const HeaderLeft = () => {
const navigation = useNavigation();
const openNavigation = () => {
navigation.openDrawer();
};
<TouchableNative /*someicon*/ onPress={openNavigator} />
I would like to know:
Is it normal that the current active screen rerenders before the drawer opens?
If normal, Is there any way around rerendering the current active screen? I tried a solution with useMemo + areEqual function with isDrawerOpen (useIsDrawerOpen) send from the parent. But that is not consistent and often gives wrong or undefined values.
Any other pointers to why this stack might be slow are greatly appreciated.
Thanks in advance!
The reason your screen is re-rendering when you open the drawer is because you're defining the drawer in the DrawerNavigator component.
This means every time React Navigation wants to find the drawer is has to reload not only the current screen, but every screen in the drawer.
Moving const Drawer = createDrawerNavigator(); outside of DrawerNavigator should solve your problem.

Pass navigation from child component to parent getting TypeError

I'm facing an issue whenever i tried to navigate to another screens. I'm using the navigation in child component and it doesn't work even i passed the props to the parent component. This is my first time on using react navigation. I've tried many possible solution yet still can't solve this issue. I'm using react navigation 5 and i need help. I'm getting an error as such :
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate')
Home.js // Parent Component
class Home extends Component {
render() {
return (
<Cards
title="In Progress"
imgUri={require('../assets/CardProgress.png')}
navigateAction={() =>
this.props.navigation.navigate('SiteAudit')
}
)
}
}
Card.js // Child Component
const Cards = (props) => {
return (
<CardContainer
backgroundColor={props.backgroundColor}
onPress={() => {
props.navigation.navigateAction;
}}>
<CardWrapper>
<CardTitle>{props.title}</CardTitle>
<CardImage source={props.imgUri} />
</CardWrapper>
</CardContainer>
);
};
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Dashboard from './screens/Dashboard';
import SiteAudit from './screens/SiteAudit';
const RootStack = createStackNavigator();
const DashboardStack = createStackNavigator();
const SiteAuditStack = createStackNavigator();
const DashboardScreen = () => {
return (
<DashboardStack.Navigator>
<DashboardStack.Screen name="Dashboard" component={Dashboard} />
</DashboardStack.Navigator>
);
};
const SiteAuditScreen = () => {
return (
<SiteAuditStack.Navigator>
<SiteAuditStack.Screen name="SiteAudit" component={SiteAudit} />
</SiteAuditStack.Navigator>
);
};
const Navigation = () => {
return (
<NavigationContainer>
<RootStack.Navigator initialRouteName="Dashboard">
<RootStack.Screen name="Dashboard" component={DashboardScreen} />
<RootStack.Screen name="SiteAudit" component={SiteAuditScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Edit your card view as follows
<Cards
title="In Progress"
imgUri={require('../assets/CardProgress.png')}
onPress={() => this.buttonPress()}
/>
const buttonPress = () => {
this.props.navigation.navigate('Site Audit');
};
Edit your button as follows,
<TouchableOpacity
style={styles.cardButton}
onPress={onPress}
<Text style={styles.cardTitle}>{this.props.title}</Text>
<Image style={styles.imageCard} source={this.props.imgUri} />
</TouchableOpacity>
Solved the issue, I just need to re-read the documentation of react navigation. I need to use the useNavigation that has been stated here https://reactnavigation.org/docs/use-navigation/
Cards.js // Child Component
const Cards = (props) => {
return (
<CardContainer
backgroundColor={props.backgroundColor}
onPress={props.navigationAction}>
<CardWrapper>
<CardTitle>{props.title}</CardTitle>
<CardImage source={props.imgUri} />
</CardWrapper>
</CardContainer>
);
};
Home.js // Parent Component
import {useNavigation} from '#react-navigation/native';
const Home = () => {
const navigation = useNavigation();
return (
<Cards
title="Completed"
backgroundColor="#0082C8"
navigationAction={() => {
navigation.navigate('Site Audit');
}}
/>
)
}

How to navigate to another screen from a function component?

I have a tabBarNavigation and i have a Home component. In the Home component i want to be able to navigate between two different screens. I have a component called ReadButton which when pressed should take me to another screen called 'BookScreen'. Now i have written so my HomeStack component have two screens 'HomeScreen' and 'BookScreen'. My problem is i cant make the 'ReadButton' to navigate to 'BookScreen' when pressed. It says ''Undefined is not an object(evaluating props.navigation.navigate).
...//This is in the main class provided by react
const HomeStack = createStackNavigator(
{
Home: HomeScreen,
Book: BookScreen,
},
config
);
const tabNavigator = createBottomTabNavigator({
HomeStack,
LinksStack,
SettingsStack,
});
tabNavigator.path = '';
export default tabNavigator;
...
//Below is the ReadButton class in a seperate file
import {withNavigation} from 'react-navigation'
const ReadButton = (props) => {
const [ReadPressed, setReadPressed] = useState(true)
const {navigate} = props.navigation.navigate
if(ReadPressed){
return(
<View>
<TouchableOpacity style={styles.buttonstyle} onPress={navigate("Home")}>
<Text style={styles.textstyle}> READ </Text>
</TouchableOpacity>
</View>
)
}
else {
return(
props.book
)
}
}
// Another class where i have my <ReadButton>
function BookCover(){
<TouchableOpacity style={styles.bottomFlexItemWhenClicked} onPress= .
{() => setBookCoverState(true)}>
<Text style={styles.BackgroundText}>{props.text}</Text>
<ReadButton book={props.book} navigation={props.navigation}>
</ReadButton>
</TouchableOpacity>)}
}
export default withNavigation(ReadButton);
Now i have tried putting 'Book: BookScreen' over 'Home: HomeScreen' and then the screen actually shows but i cant navigate between them. What am i missing?
Maybe you can try hooks?
install hooks: yarn add react-navigation-hooks#alpha
and then use it in your function component:
import { useNavigation } from 'react-navigation-hooks'
export default function Screen1 () {
const { navigate } = useNavigation()
return (
<Button
title='Try'
onPress={() => { navigate('Screen2') }}
/>
)
}
Please note that components inside the HomeScreen or BookScreen won't receive the navigation prop.
1.Either you send them manually like this
<ReadButton navigation={this.props.navigation}/>
or
2.Use the built in withNavigation of react-navigation like this
import {withNavigation} from 'react-navigation'
const ReadButton=(props)=>{}//Now you can access navigation prop here
export default withNavigation(ReadButton)
Second method will come to handy when your component is deeply nested and you want to access the navigation prop without manually passing the props.

How to access navigation outside main component?

I've been working with the default tabs project created with create-react-native-app.
So I created my own screen where this.props.navigation is accessible in the main (export default class) component. It works fine to do navigate('Search') from the button titled 'nav default'.
However, after many tries, I couldn't navigate from either the button in the headerRight or in my custom component MyComponent.
How do I change my alerts to instead do navigate('Search') like the main component?
import React from 'react';
import { Button, Dimensions, ScrollView, StyleSheet, Text, View } from 'react-native';
class MyComponent extends React.Component {
// i wish i could navigate from here
render() {
//const { navigate } = this.props.navigation; // this causes TypeError undefined is not an object (evaluating this.props.navigation.navigate)
return (
<View>
<Button title="nav from component" onPress={() => alert('This should navigate, not alert')} color="red" />
</View>
);
}
}
export default class MyScreen extends React.Component {
// i wish i could navigate from here too
static navigationOptions = {
title: 'Test',
headerRight: (
//<Button onPress={() =>navigate('Search')} /> // how do I achieve this?
<Button title="nav from header" onPress={() => alert('This should navigate, not alert')} />
),
};
render() {
const { navigate } = this.props.navigation;
return (
<ScrollView style={styles.container}>
<Button onPress={() =>navigate('Search')} title="nav default" />
<MyComponent />
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 15,
backgroundColor: '#fff',
},
});
There are two different problems. First, navigation is only passed as a prop to the components that are navigation screens. So, to access it from other components, such as your MyComponent, you have to pass it through props <MyComponent navigation={navigation} /> and then you can use this.props.navigation.navigate inside it. You can also use the withNavigation higher order component, but in that case the first approach is better.
The other problem is, if you want to access the navigation prop in navigationOptions, you should define it as a function, and not as an object. The implementation would be something like this:
static navigationOptions = ({ navigation }) => ({
title: 'Test',
headerRight: (
<Button onPress={() =>navigation.navigate('Search')} />
),
});