How To Call Our Own Build withNavigation In React Native 6 - react-native

Help,
Firstly, did React Native 6 or 5 have withNavigation ? I cannot find it in the documentation in React Native website.
So, I found some thread that we can build our own withNavigation like this :
withNavigation.js
import React from 'react';
import { useNavigation } from '#react-navigation/native'; // not sure package name
export const withNavigation = (Component) => {
return (props) => {
const navigation = useNavigation();
return <Component navigation={navigation} {...props} />;
};
};
But I don't know how to call or use the own build withNavigation.
This is my source code :
ResultList.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
import ResultsDetail from './ResultsDetail';
import { withNavigation } from '../helper/withNavigation';
const ResultList = ({ title, results }) => {
console.log(withNavigation.component);
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={(results) => results.id}
renderItem={({item}) => {
return (
<TouchableOpacity onPress={() => withNavigation('ResultShowScreen')}>
<ResultsDetail results={item} />
</TouchableOpacity>
)
}}
/>
</View>
)
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 5,
},
container: {
marginBottom: 10
}
});
export default ResultList;
In here :
console.log(withNavigation.component)
it show : undefined
How to get the navigation props with our own withNavigation ?
Thank You

Since your helper function, withNavigation, is a Higher-Order Component (HoC) you have to wrap your component with it in order to be able to use it:
const ResultList = ({ title, results, navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={(results) => results.id}
renderItem={({ item }) => {
return (
// Now you can use the navigation prop inside of your component
<TouchableOpacity onPress={() => navigation.navigate('MyRoute')}>
<ResultsDetail results={item} />
</TouchableOpacity>
);
}}
/>
</View>
);
};
// withNavigation adds the navigation property to your ResultList component's properties
export default withNavigation(ResultList);
I think the reason why you can't find withNavigation inside of React Navigation 5 or 6 anymore is because they suggest you to use their built-in hooks instead. I am not sure why do you need to use a custom made HoC when you could just use the useNavigation hook. If you don't have a specific reason to use your custom HoC, the built-in hooks could simplify your code:
import { useNavigation } from '#react-navigation/native';
const ResultList = ({ title, results }) => {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={(results) => results.id}
renderItem={({ item }) => {
return (
// You can use the navigation property inside of your component
<TouchableOpacity onPress={() => navigation.navigate('MyRoute')}>
<ResultsDetail results={item} />
</TouchableOpacity>
);
}}
/>
</View>
);
};
export default ResultList;

As per the upgrading documentation here
React Navigation 4.x included higher order components such as
withNavigation and withNavigationFocus. Now they live in the compat
package.
The documentation says that in order to upgrade from React Navigation 4 to 5:
withNavigation HOC is moved to another package #react-navigation/compat
So, in order to use that you have to import that package first:
import { withNavigation } from '#react-navigation/compat';

Related

How to execute onPress on TouchableOpacity react-native using jest and #testing-library/react-native?

I have a component called Header that look like this:
import React from 'react'
import {StyleSheet, TouchableOpacity, View, StyleProp, ViewStyle} from 'react-native'
import {Text} from '..'
import Icon from 'react-native-vector-icons/MaterialIcons'
import {theme} from '#app/presentations/utils/styles'
import {useNavigation} from '#react-navigation/core'
interface IHeaderProps {
title: string
headerRight?: () => JSX.Element | false | undefined
onGoBack?: () => void
hideBackButton?: boolean
style?: StyleProp<ViewStyle>
}
const Header: React.FC<IHeaderProps> = props => {
const navigation = useNavigation()
const goBack = () => {
props.onGoBack ? props.onGoBack : navigation.goBack()
}
return (
<View style={[styles.container, props.style]}>
<View style={styles.leftContent}>
{props?.hideBackButton ? null : (
<TouchableOpacity onPress={goBack} testID="headerBackButton">
<Icon name={'chevron-left'} size={22} color={theme.colors.black} />
</TouchableOpacity>
)}
</View>
<View style={{flex: 1, flexGrow: 10, alignItems: 'center'}}>
<Text maxLines={2} style={{paddingHorizontal: 8, textAlign: 'center'}} type="semibold">
{props.title}
</Text>
</View>
<View style={styles.rightContent}>{props.headerRight && props.headerRight()}</View>
</View>
)
}
export default Header
Focus on TouchableOpacity, I want to fire the onPress of it using testId, but looks like it won't fire.
it('Should have correct behavior', () => {
const goBackFn = jest.fn()
const props: IHeaderProps = {
title: 'My Header',
onGoBack: goBackFn,
}
const {component, getByTestId, queryAllByText} = renderComponent(props)
expect(component).toMatchSnapshot()
expect(queryAllByText('My Header').length).toBe(1)
expect(getByTestId('headerBackButton')).toBeTruthy()
fireEvent.press(getByTestId('headerBackButton'))
expect(goBackFn).toBeCalled()
})
The error message was like this
means that my goBack function never executed. I wondering why.
Then I check the snapshots of my Header component, it is not show TouchableOpacity but it shows View with onClick function on it
<View
accessible={true}
collapsable={false}
focusable={true}
nativeID="animatedComponent"
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
testID="headerBackButton"
>
My question is how do I execute onPress on TouchableOpacity ?
I fixed this. At least there is two problem from my implementation.
On the Header component, I forgot to add parenthesis () on props.onGoBack function. It should be props.onGoBack() not props.onGoBack
I need to add await waitFor(() => { ...wait for my getTestById to be truthy })

How to pass selected data to another screen from Flatlist

I am still new in using React Native and Mobile Apps Development. I tried to copy the code from another tutorial and have little bit of understanding it.
I have Save.js, Feed.js and Details.js. I have successfully retrieved the data from Save.js to Feed.js using FlatList and RenderItem. Now, I want to pass only selected data from Feed.js to Details.js. But I am confused which way to use, whether useNavigation, getParam, withNavigation or anything else? And is there any difference between using Hooks and Class? Btw I'm using Hooks.
Save.js
import { View, TextInput, Image, Button, StyleSheet, TouchableOpacity, Text} from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
export default function Save(props, navigation) {
const [productName, setProductName] = useState("")
const [category, setCategory] = useState("")
return (
<View style={styles.inputView}>
<TextInput
placeholder="Product name..."
onChangeText={(productName) => setProductName(productName)}
/>
</View>
<View style={styles.inputView}>
<TextInput
placeholder="Category..."
onChangeText={(category) => setCategory(category)}
/>
</View>
Feed.js
function Feed(props, navigation) {
const { currentUser, posts } = props;
const { navigate } = useNavigation();
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
Details.js
export default function Details({ props, navigate, route }) {
const productName = props.navigation.route.params.productName;
const { navigate } = useNavigation();
const productName = useNavigationParam('productName');
return (
<View>
<Text>{productName}</Text>
<Text>{Category}</Text>
</View>
)
}
I am not sure which way to use in Details.js, so I just put all code I have used and tested.
the code bellow will help you and I think you have problem in destructing context this will help you. and remember navigation is an object inside props
Feed.js
function Feed(props) {
const { currentUser, posts, navigation } = props;
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
in Feed you dont need to use useNavigation() because props argument contain navigation.
Details.js
export default function Details(props) {
const {productName, category} = props.navigation.route.params;
return (
<TouchableOpacity onPress={()=>props.navigation.navigate("Save",{productName, category})}>
<Text>{productName}</Text>
<Text>{Category}</Text>
</TouchableOpacity>
)
}

React Navigation goBack()

I got some error when i have used goBack(null) method in functional component in react navigation
function Header({ titleText, navigation}) {
return (
<Appbar.Header style={styles.headerContainer}>
<Appbar.BackAction style={styles.backButton} onPress={() => navigation.goBack(null)} >
<Text style={styles.back}></Text>
</Appbar.BackAction>
<View style={styles.container}>
<Title style={styles.title}>{titleText}</Title>
</View>
</Appbar.Header>
)}
Try with useNavigation hook from #react-navigation/native
import * as React from 'react';
import { Text } from 'react-native';
import { useNavigation } from '#react-navigation/native';
function MyText() {
const navigation = useNavigation();
return <Text onPress={() => navigation.goBack()}>Go Back</Text>;
}

Basic simple React Navigation v5 navigating between screens

I've spent 2 days search, reading and find lots of v3 and v4 class based examples for how to handle navigation in React Native Navigation.
All I want to achieve is to move between 2 of my screens using react native navigation. My App.js contains the Tab navigator and that works fine. The tab opens up a component (screen) called Mens and from there I want to be able to open up a PDP page that passes in properties of an article ID.
I have tried numerous ways of wiring up the application to allow this; I've read all the react native documentation and tried a number of approaches;
Created a seperate file to include the naviagtion stack;
import * as React from 'react'
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
import News from './news';
import Mens from './mens'
import Watch from './watch'
const Stack = createStackNavigator()
function MainStackNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="News" component={News} />
<Stack.Screen name="Mens" component={Mens} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default MainStackNavigator
But when I try to use one of the screens, I get an error. The onpress I try is;
<TouchableOpacity onPress={() => navigation.navigate('Mens')}>
I have also tried to move the NavigationContainer / Stack Navigator code into the News component, but I haven't manage to make that work.
The flow that I want is simple enough; App.js has my tabs, 5 tabs that navigate to the main screens and then on each of those, people can click on a list item in a flat list (which displays a summary) to read the full article.
The news.js file content is below;
import React, { useEffect, useState } from 'react';
import { ActivityIndicator, FlatList, Image, ListItem, Text, View, StyleSheet, ScrollView, TouchableOpacity, Alert} from 'react-native';
import Moment from 'moment';
import MatchReports from './matchreports.js';
import navigation from '#react-navigation/native';
const News = (props) => {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
function chkValue(val) {
if(val.length == 0){
val = 'https://derbyfutsal.files.wordpress.com/2019/07/banner-600x300.png';
}else{
val = val;
}
return val;
}
useEffect(() => {
fetch('https://xxxx')
.then((response) => response.json())
.then((json) => {
setData(json)
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<ScrollView>
<View style={styles.body}>
<Text style={styles.titlesnopadding}>Watch</Text>
<View style={{height:200}}>
<Watch />
</View>
<Text style={styles.titles}>Match reports</Text>
<View style={{height:100}}>
<MatchReports typeOfProfile='Men'/>
</View>
<Text style={styles.titles}>Latest News</Text>
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={data}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<View>
<TouchableOpacity onPress={() => navigation.navigate('Mens')}>
<Image style={styles.img} source={{ uri: chkValue(item.jetpack_featured_media_url) }} />
<View>
<Text style={styles.textbck}>{item.title.rendered.replace(/<\/?[^>]+(>|$)/g, "")}</Text>
<Text style={styles.summary}>{item.excerpt.rendered.replace(/<\/?[^>]+(>|$)/g, "")}{"\n"}{Moment(item.date, "YYYYMMDD").fromNow()}</Text>
</View>
</TouchableOpacity>
</View>
)}
/>
)}
</View>
</ScrollView>
);
};
Any help is appreciated, as I've read so many using class instead of functional programming and out of date navigation that it's been challenging working it out.
EDIT:
I had missed the props.navigation.navigate('Mens'), which works fine now. Though its only half solves my problem.
I have the following inside my app.js;
import 'react-native-gesture-handler';
import React from 'react';
import { View, StyleSheet, Image } from 'react-native';
import News from './components/news';
import Shop from './components/shop';
import Mens from './components/mens';
import Fixtures from './components/fixtures';
import Ladies from './components/ladies';
import Pdp from './components/pdp'
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
const App = () => {
return (
<View style={styles.header}>
<View>
</View>
<View style={styles.body}>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="News" component={News} />
<Tab.Screen name="Mens" component={Mens} />
<Tab.Screen
name="icon"
component={Shop}
options={{
title: '',
tabBarIcon: ({size,focused,color}) => {
return (
<Image
style={{ marginTop:20,width: 80, height: 80 }}
source={{
uri:
'https://derbyfutsal.files.wordpress.com/2020/05/derby-futsal-logo-2019.png',
}}
/>
);
},
}}
/>
<Tab.Screen name="Ladies" component={Ladies} />
<Tab.Screen name="Fixtures" component={Fixtures} />
</Tab.Navigator>
</NavigationContainer>
</View>
</View>
)
};
const styles = StyleSheet.create({
header: {
marginTop: 20,
height:0,
flex: 1
},
body: {
flex:2,
flexGrow:2,
},
nav: {
fontSize: 20,
},
});
export default App;
Anything thats been set as tab Screen in this works just fine if I reference it in my news.js screen, but I don't want to declare PDP.js in this as I don't want it to display as a tab.
Instead once a user has gone to a screen using the tab navigation, the user then clicks on a item in the flatlist and it opens up pdp.js.
In many ways, once someone has opened up the main categories (as seen on the tab navigation) and clicked on an item in the flatlist, all I want to do is;
<a href="pdp.js?id=xxxxx">
https://reactnavigation.org/docs/navigation-actions/#navigate
import { CommonActions } from '#react-navigation/native';
navigation.dispatch(
CommonActions.navigate({
name: 'Profile',
params: {
user: 'jane', // props.route.params.user
},
})
);

Getting undefined is not an object (evaluating '_this.props.navigation')

I'm using DrawerNavigator and I have 3 pages: Router page, mainScreen and a photos page.
I maked a header navbar area and I used This <TouchableHighlight onPress={() => this.props.navigation.navigate('DrawerOpen')}> to open Drawer menu in mainScreen and used that for photos page too, menu is ok in mainScreen but when I click <TouchableHighlight onPress={() => this.props.navigation.navigate('DrawerOpen')}> in photos page, I get the error:
undefined is not an object (evaluating '_this.props.navigation')
How can I fix that?
My photos page:
import React from 'react';
import { Button, ScrollView, View, Text, StyleSheet, TouchableHighlight } from 'react-native';
import { DrawerNavigator } from 'react-navigation';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import Icon from 'react-native-vector-icons/FontAwesome'
const MyNavScreen = ({ navigation }) => (
<View>
<View style={styles.containerNavbar}>
<TouchableHighlight onPress={() => this.props.navigation.navigate('DrawerOpen')}>
<Icon name="bars" size={30} color="#fff" />
</TouchableHighlight>
<Text style={styles.navbarTitle}>Photos</Text>
</View>
<ScrollView>
<View><Text>photo</Text></View>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
</ScrollView>
</View>
);
const MyPhotosHomeScreen = ({ navigation }) => (
<MyNavScreen navigation={navigation} />
);
MyPhotosHomeScreen.navigationOptions = {
title: 'Photos',
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="photo"
size={24}
style={{ color: tintColor }}
/>
),
};
export default MyPhotosHomeScreen;
mainScreen:
export default class MainScreen extends React.Component {
static navigationOptions = {
drawerLabel: 'Home',
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="home"
size={24}
style={{ color: tintColor }}
/>
)
};
render() {
return (
<View>
<View style={styles.containerNavbar}>
<TouchableHighlight onPress={() => this.props.navigation.navigate('DrawerOpen')}>
<Icon name="bars" size={30} color="#fff" />
</TouchableHighlight>
<Text style={styles.navbarTitle}>mainScreen</Text>
</View>
<View>
<View style={styles.containerFooter}>
<Text style={styles.footerTitle}>Footer</Text>
</View>
</View>
</View>
)
}
}
If you are using navigation in child component don't forget to send navigation in props to child
<ChildComponent navigation={this.props.navigation}/>
Access in child component like this
props.navigation.navigate("ScreenName")
Perhaps I'm overlooking something, but it just looks like a simple Javascript error. You're destructing your props in your pure component MyNavScreen:
const MyNavScreen = ({ navigation }) => (
This means that you don't have access to this.props. You just have access to the destructured prop navigation. Hence the reason for the undefined error as this really is undefined:
<TouchableHighlight onPress={() => this.props.navigation.navigate('DrawerOpen')}>
If you change it instead to use navigation directly, it should work:
<TouchableHighlight onPress={() => navigation.navigate('DrawerOpen')}>
On mainScreen, you are fine because it's not a pure component with destructured arguments. So you still have access to this.props in render().
You should brush up on destructing if this is causing you trouble.
Binding this worked for me
In my case it worked when I bind this to the method that calls the prop it in the constructor.
constructor(props){
super(props);
this.showDetails = this.showDetails.bind(this);// you should bind this to the method that call the props
}
showDetails(_id){
this.props.navigation.navigate('Details');
}
for functional components try useNavigation hook for deep navigation
Or Simply use arrow function
showDetails = (_id) => {
this.props.navigation.navigate('Details');
}
because while you use expression function it is going to create it's own scope.
Try this:
import { withNavigation } from 'react-navigation';
withNavigation serves the props all over the project/app, you access the navigation props from anywhere.
and
onPress={() => this.props.navigation.navigate('DrawerOpen')}
Finally,
export default withNavigation(MyPhotosHomeScreen);
check out this https://reactnavigation.org/docs/en/connecting-navigation-prop.html
If you use TouchableOpacity/Height in your child element, pass it this.props.onPress like this:
<TouchableOpacity onPress={this.props.onPress}/>
Then call the onPress function in your parent component like this:
<Parent onPress={this.Handlepress} />
very simple: follow some steps here
import {useNavigation} from '#react-navigation/native';
const Home =()=>{
//add this line under function scope.
const navigation = useNavigation();
return(
<TouchableOpacity onPress={() => navigation.navigate('Detail')}>
//your code.......................
)
}
This is how I have done it in React Navigation 2 release: I call the openDrawer() method from a StackNavigator that is a child navigator of the DrawerNavigator.
This is my DrawerNavigator:
export const Drawer = DrawerNavigator(
{
MyAccount: {screen: TabsStack},
});
export const TabsStack = StackNavigator({
Tabs: {
screen: Tabs, navigationOptions: ({navigation}) => ({
headerLeft: (
<TouchableOpacity style={{marginLeft: 10, marginTop: 3}}
onPress={() => navigation.openDrawer()}>
<Image source={require('./assets/menu_h.png')}/>
</TouchableOpacity>)
})
functional components take props as argument.
You should try this
const MyNavScreen = ({props}) =>
and then call the props without this key word
onPress = {() => props.navigation.navigate('DrawerOpen')}
If you want navigation in child component,then you have to get props in child component.
Suppose you have 3 components - Comp_1,Comp_2,Comp_3 and you want to navigate from Comp_2 -> Comp_3. To do this follow these steps.
Pass props in Comp_1 component.Like this
<Comp_2 navigation={this.props.navigation}/>
Now in Comp_2, we can navigate from Comp_2 -> Comp_3 like this.
this.props.navigation.navigate('Comp_3');
For example -
<Button
onPress = {() => this.props.navigation.navigate('Comp_3')}
title = 'Go to Comp_3 Screen'
/>
In then compnent that you're trying enter to another view you must send the object navigation by you use this in the onPress of component imported
example
Implementing component in a view
<CardComponent title="bla bla" navigation={this.props.navigation} />
Component template
<View>
<Button title={this.props.title} onPress={()=>
this.props.navigation.navigate("anotherAwesomeView")}/>
</View>
This problem is because the component that you're trying implement is not defined on stackNavigation, and by this the methot navigation is not avalible for you, and passing this object navigator by params you'll can access to it
For those who are not using class components and prefer functional components:
First import useNavigation then from the screen that you wish to navigate from, initiate (declare) the variable navigation = useNavigation();
import { useNavigation } from '#react-navigation/native';
function ScreenToNavigateFrom() {
const navigation = useNavigation();
return(
<TouchableOpacity onPress={() => navigation.navigate('AnotherScreen')}>
<Text style={{fontWeight: "bold", color:"white" }} >Performance</Text>
</TouchableOpacity>
);
}
This answer is courtesy of this gisthub issue: https://github.com/react-navigation/react-navigation/issues/7961
i had the same problem when i was using the header component
now you can use the navigation variable in other component like this
<TouchableOpacity onPress={() => { this.props.navigation.navigate("Play");}}>
Happy Codding :)
try this instead you might be missing to import useNavigation() from '#react-navigation/native';
import { useNavigation } from '#react-navigation/native';
function NotificationsScreen() {
const navigation = useNavigation();
return(
<Button
onPress={() => navigation.navigate('Notifications')}
title="Go to notifications"
/>
);
}
enter code here
I encountered the same problem. That's how I solved it:
Verify that all your constructors have "props" has argument
Bind the function with use the function this.props.navigation.navigate in the constructor like this: this.your_function = this.your_function.bind(this)
when you defined screen in createStackNavigator , it by default pass a props called navigation,
something like this => navigation={this.props.navigation}
but when you using this.props.navigation.navigator("YOUR SCREEN ")
and didn't defined this screen at createStackNavigator you must pass the navigation={this.props.navigation} form the screen that you defined in createStackNavigator and then you can use it in your component .
class ProductScreen extends Component {
export default ProductScreen; // make sure bottom this line mention
Call this
<TouchableOpacity
onPress = {() => this.props.navigation.navigate('ProductAddScreen')}
activeOpacity={0.7} style={styles.button}>
<Text style={styles.message}>{this.state.notFound} </Text>
</TouchableOpacity>