I'm trying to open a navigation draw from a stack navigation header button. The header button is showing up fine but when I click the button I am getting
undefined is not an object (evaluating '_this.props.navigate')
I can't seem to find a solid example of how to do this or if its even possible with react navigation.
import React, { Component } from 'react';
import { createStackNavigator, NavigationActions } from 'react-navigation'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { Draw } from './DrawNav.js'
export const RootStack = createStackNavigator (
{
DrawNav: {
screen: Draw,
navigationOptions: ({ navigation }) => ({
//Hide the shadow of the header
headerStyle: {
elevation:0,
shadowColor: 'transparent',
shadowRadius: 0,
shadowOffset: {
height: 0,
}
},
headerLeft: (
<View style={{marginLeft: 10}}>
<Icon
name="menu"
size={25}
color="#D4AF37"
onPress={() => this.props.navigation.openDrawer()}
/>
</View>
),
})
},
},
);
this.props is only used in a react class. I assume you're using react-navigation v2 then you should dispatch DrawerAction like below
import React, { Component } from 'react';
import { createStackNavigator, NavigationActions } from 'react-navigation'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { DrawerActions } from 'react-navigation';
import { Draw } from './DrawNav.js'
export const RootStack = createStackNavigator (
{
DrawNav: {
screen: Draw,
navigationOptions: ({ navigation }) => ({
//Hide the shadow of the header
headerStyle: {
elevation:0,
shadowColor: 'transparent',
shadowRadius: 0,
shadowOffset: {
height: 0,
}
},
headerLeft: (
<View style={{marginLeft: 10}}>
<Icon
name="menu"
size={25}
color="#D4AF37"
onPress={() => navigation.dispatch(DrawerActions.openDrawer())}
/>
</View>
),
})
},
},
);
Related
I want to test a basic component name TitleHeader which uses HStack and VStack from native base.
The component:-
import {Center, HStack, VStack} from 'native-base';
import React from 'react';
import {View, Text} from 'react-native';
import appColors from '../../constants/appColors';
//Icon Imports
import Ionicons from 'react-native-vector-icons/Ionicons';
import {TouchableOpacity} from 'react-native-gesture-handler';
import {useNavigation} from '#react-navigation/core';
const TitleHeader = ({
title = 'Header Title',
navigationBack = false,
center = false,
onBackPress,
drawer = false,
style = {},
}) => {
console.log(title);
const navigation = useNavigation();
const handleGoBack = () => {
if (onBackPress && typeof onBackPress === 'function') {
onBackPress();
} else if (drawer) {
navigation.goBack();
} else {
navigation.pop();
}
};
return (
<HStack
bg={appColors.primaryBlue}
style={[
{
height: 55,
position: 'relative',
},
style,
]}
alignItems="center"
px={3}>
{navigationBack ? (
<View style={{position: 'absolute', zIndex: 10, left: 10}}>
<TouchableOpacity
onPress={handleGoBack}
style={{
width: 35,
height: 35,
justifyContent: 'center',
alignItems: 'center',
}}>
<Ionicons name="arrow-back" size={26} color="white" />
</TouchableOpacity>
</View>
) : null}
<VStack flex={1} alignItems="center" pl={2}>
<Text
color="white"
fontSize="lg"
numberOfLines={1}
ellipsizeMode="tail"
style={
center
? {}
: {
width: '80%',
}
}>
{title}
</Text>
</VStack>
</HStack>
);
};
export default TitleHeader;
Following is my test case which uses jest for testing:-
import React from 'react';
import {render} from '#testing-library/react-native';
import TitleHeader from '../src/components/AppHeaders/TitleHeader';
import renderer from 'react-test-renderer';
import {NavigationContainer} from '#react-navigation/native';
import {NativeBaseProvider} from 'native-base';
jest.mock('native-base');
const wrapper = ({children}) => (
<NativeBaseProvider
initialWindowMetrics={{
frame: {x: 0, y: 0, width: 0, height: 0},
insets: {top: 0, left: 0, right: 0, bottom: 0},
}}>
{children}
</NativeBaseProvider>
);
describe('Testing Title Header for screens', () => {
test('should render title header', () => {
const tree = renderer
.create(
<NavigationContainer>
<TitleHeader title={'HEADER TEST'} />
</NavigationContainer>,
{wrapper},
)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
But I am getting this error inspite of using jest.mock('native-base') in my jest.setup.js.
I am new to testing please help me render this component first and test it using jest. Also, one more thing to add is that if I do not use the wrapper function it throws error telling me that "theme is not defined. Did you forget to wrap your app inside NativeBaseProvider?".
If the component that you trying to test has the theme coming from NativeBase you need to provide the <ThemeProvider/> (Spelling might differ depending on the library, in your case <NativeBaseProvider/>
If you do jest.mock('native-base') and you do not return anything will cause Nothing was returned from render error.
Did you add native-base library to transformIgnorePatterns of jest.config as suggested in this issue?
I want when I click button inside access file remove the same item in months array inside Home file.
How can I solve this?
App.js
this file conncet screen.
import {StyleSheet, View} from 'react-native';
import {createStackNavigator} from '#react-navigation/stack';
import Home from './src/components/Home';
import {NavigationContainer} from '#react-navigation/native';
import Access from './src/components/access';
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="access" component={Access} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
ButtonCicle.js
this file make circle.
import { View, Text,TouchableOpacity } from 'react-native';
import styles from './Styles/ButtonCircleStyles';
export default function ButtonCicle({BtnName, onPress}) {
return (
<TouchableOpacity style={styles.buttonContainer} onPress={onPress}>
<Text style={styles.button}>{BtnName}</Text>
</TouchableOpacity>
);
}
ButtonCircleStyles.js
this file make style
import {StyleSheet, Dimensions} from 'react-native';
const height = Dimensions.get('window').height;
const width = Dimensions.get('window').width;
const styles = StyleSheet.create({
buttonContainer:
{
backgroundColor: 'red',
borderRadius: 50,
width: width / 5,
height: width / 5,
justifyContent: 'center',
marginStart: '4%',
marginBottom: '4%',
},
button:
{
fontSize: 25,
textAlign: 'center',
},
});
export default styles;
Home.js
this file make array and onPress to another screen
import React from 'react';
import {StyleSheet, View} from 'react-native';
import ButtonCicle from './ButtonCircle';
const Home = ({navigation}) => {
const months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const listbutton = months.map((i, key) => (
<ButtonCicle
BtnName={i}
key={key}
onPress={() => navigation.navigate('access') || alert(i)}
/>
));
return (
<>
<View style={styles.container}>{listbutton}</View>
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ecf0f1',
padding: 8,
marginTop: '10%',
flexDirection: 'row',
height: 300,
flexWrap: 'wrap',
},
});
export default Home;
access.js
import React from 'react'
import { View, Text, Button } from 'react-native'
export default function access({navigation}) {
return (
<View>
<Text>Home</Text>
<Button title='Click' onPress={() => navigation.navigate('Home')} />
</View>
)
}
I want when I click button inside access file remove the same item in months array inside Home file.
How can I solve this?
How create it ?
You can try with a callback. Define a function inside Home.js that remove the element from the array. Then you could pass this callback as a route param
navigation.navigate('access', {deleteItem: //your callback})
And in the access.js file you get this callback
const deleteItem = navigation.getParam('deleteItem');
And call it before navigate back to home
const onPress = () => {
deleteItem();
navigation.navigate('Home');
}
<Button title='Click' onPress={onPress} />
Could you please help me on this small issue.
I am very very new to React native and here i am training in learn react navigation. My problem is Drawer navigator is not working when i try to swap from left to right. But bottom navigator is working. Any idea whats wrong with this please.
Also: if you can please send me a good sample to learn this area.
I have some samples, but most of the samples are outdated.
MyCode:
import React from 'react';
import {TouchableOpacity, View, Text, Image} from 'react-native';
import {createAppContainer, createSwitchNavigator} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import {createBottomTabNavigator} from 'react-navigation-tabs';
import {createDrawerNavigator} from 'react-navigation-drawer';
import HomeScreen2 from '../screens/HomeScreen2';
import HomeScreen from '../screens/HomeScreen';
import Setup from '../screens/Setup';
const HomeStack = createStackNavigator(
{
//Defination of Navigaton from home screen
Home: { screen: HomeScreen },
// Details: { screen: ProfileScreen },
},
{
defaultNavigationOptions: {
//Header customization of the perticular Screen
headerStyle: {
backgroundColor: '#42f44b',
},
headerTintColor: '#FFFFFF',
title: 'Home',
//Header title
},
}
);
const SettingsStack = createStackNavigator(
{
//Defination of Navigaton from setting screen
Settings: { screen: Setup },
},
{
defaultNavigationOptions: {
//Header customization of the perticular Screen
headerStyle: {
backgroundColor: '#42f44b',
},
headerTintColor: '#FFFFFF',
title: 'Settings',
//Header title
},
}
);
const TabMain = createBottomTabNavigator(
{
TAB1: HomeStack,
TAB2: SettingsStack,
},
{
initialRouteName: 'TAB1',
}
)
const DrawHomeView = createStackNavigator({
HomeV: {screen: HomeScreen2}},
{
headerMode: 'float',
navigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: 'rgb(255,45,85)',
paddingLeft: 10,
paddingRight: 10
},
title: 'Map View',
headerTintColor: 'white',
headerLeft: <View>
<TouchableOpacity
onPress={() => {
if (navigation.state.isDrawerOpen === false) {
navigation.dispatch(DrawerActions.openDrawer());
} else {
navigation.dispatch(DrawerActions.closeDrawer());
}
}}>
<Text>Menu</Text>
</TouchableOpacity>
</View>
})
});
const DrawSettingsView = createStackNavigator({
SettingsV: {screen: Setup}},
{
headerMode: 'float',
navigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: 'rgb(255,45,85)',
paddingLeft: 10,
paddingRight: 10
},
title: 'Map View',
headerTintColor: 'white',
headerLeft: <View>
<TouchableOpacity
onPress={() => {
if (navigation.state.isDrawerOpen === false) {
navigation.dispatch(DrawerActions.openDrawer());
} else {
navigation.dispatch(DrawerActions.closeDrawer());
}
}}>
<Text>Menu</Text>
</TouchableOpacity>
</View>
})
});
const TabMain2 = createDrawerNavigator(
{
TAB3: DrawHomeView,
TAB4: DrawSettingsView,
},
{
initialRouteName: 'TAB3',
}
)
export default createAppContainer(createSwitchNavigator(
{
Sub1: TabMain,
Sub2: TabMain2,
},
{
initialRouteName: 'Sub2',
}
));
Okay. After some dabbling with your code and the docs : https://reactnavigation.org/docs/nesting-navigators/
and please read the docs and try their examples as they are excellent descriptive and available for real time execution on snack.
For the second part of the question, i put together an example for you:
import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//Import necessary components
//Create Screens, Dummy screens in this case.
function NotificationsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.goBack()} title="Go back home" />
</View>
);
}
function Feed ({ navigation, route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{fontSize: 40}}>Feed </Text>
</View>
);}
function Home ({ navigation, route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{fontSize: 40}}> Home </Text>
</View>
);}
// Here is your questions answer, just create a Bottom Navigator
// and a Drawer Navigator and nest them in each other after declaring your
// screens.
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
function HomeScreen({ navigation }) {
return (
<Tab.Navigator>
//Put your Tab screens here.
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Feed" component={Feed} />
</Tab.Navigator>
);
}
export default function App() {
return (
// For the main export create a navigation container and declare the
// Drawer Navigator inside the main navigation container, then use that to
// To Access your Tab navigator "HomeScreen" and put whatever else you
// Want in your Drawer Navigator.
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
Explanation: You are only allowed to nest navigators within each other and not within the main navigation container.
This code has been tested using expo-cli 3.18.4
I'm trying to add a tabbed navigation to a React Native app, also using expo if that matters, and whenever I do this I get a big chunk of white at the top of the screen. I'm not seeing what's causing this though, the status bar background shouldn't be pushing it down and as far as I can tell changing the height style of my navigation or main view doesn't do anything.
This looks like:
The complete source to recreate this is:
// utils/colors.js
export const white = '#fff'
export const orange = '#f26f28'
// App.js
import React, { Component } from 'react'
import { StyleSheet, View, StatusBar, Dimensions } from 'react-native'
import {
createMaterialTopTabNavigator,
createStackNavigator
} from 'react-navigation'
import { Constants } from 'expo'
import List from './components/List'
import Add from './components/Add'
import { orange, white } from './utils/colors'
function StatusBarBackground ({ backgroundColor, ...props }) {
return (
<View style={{ backgroundColor, height: Constants.statusBarHeight }}>
<StatusBar translucent backgroundColor={backgroundColor} {...props} />
</View>
)
}
const Tabs = createMaterialTopTabNavigator({
List: {
screen: List,
navigationOptions: {
tabBarLabel: 'List',
},
},
Add: {
screen: Add,
navigationOptions: {
tabBarLabel: 'Add',
},
},
}, {
navigationOptions: {
header: null
},
tabBarOptions: {
activeTintColor: white,
style: {
height: 56,
backgroundColor: orange,
shadowColor: 'rgba(0, 0, 0, 0.24)',
shadowOffset: {
width: 0,
height: 3
},
shadowRadius: 6,
shadowOpacity: 1
}
}
})
const MainNavigator = createStackNavigator({
Home: {
screen: Tabs,
},
})
export default class App extends Component {
render() {
return (
<View style={{flex: 1}}>
<StatusBarBackground backgroundColor={orange} barStyle="light-content" />
<MainNavigator />
</View>
)
}
}
The Add and List components are just a view with a text, both look similar to this:
import React, { Component } from 'react'
import { View, Text } from 'react-native'
class Add extends Component {
render() {
return (
<View>
<Text style={{fontSize: 20}}>Add</Text>
</View>
)
}
}
export default Add
You are adding Your Tab Navigator inside Stack Navigator, That is why that white header is showing from Stack Navigator.
1) If You want to add header then style your header like below
const MainNavigator = createStackNavigator({
Home: {
screen: Tabs,
},
},{
navigationOptions:{
headerStyle : {
backgroundColor:'#243346'
},
headerTintColor:"#fff"
},
})
2) If you want to remove header then you can either remove your Tab Navigator from Stack Navigator or add headerMode:'none' inside navigationOprions object.
I'm trying to navigate from tab page to other page. I'm following tabnavigator and stacknavigator.
myTabsHost(RegisterHostPage) Here I'm hosting two tabs
import React, { Component } from "react";
import {
AppRegistry,
Text,
View,
Image,
StyleSheet,
TouchableOpacity
} from "react-native";
import { TabNavigator } from "react-navigation";
import { Tab } from "../../config/router.js";
import {Tab2} from "../../config/router.js";
import { NavigationActions } from "react-navigation";
class RegisterHost extends Component {
render() {
return (
<View style={Styles.container}>
<Text style={Styles.boldLabel}> User Register</Text>
<Text style={Styles.normalLabel}>Wallet account registration</Text>
<TouchableOpacity
onPress={() => {
const navigateAction = NavigationActions.navigate({
key: null,
routeName: "preactivate",
params: {}
});
this.props.navigation.dispatch(navigateAction);
}}
>
<Text>topreactivate</Text>
</TouchableOpacity>
<Tab2 />
</View>
);
}
}
const Styles = StyleSheet.create({
container: {
flex: 1,
padding: 2,
justifyContent: "center",
backgroundColor: "#FFFFFF"
},
boldLabel: {
fontWeight: "bold",
fontSize: 24,
alignSelf: "center",
color: "#08AE9E",
marginBottom: 20,
marginTop: 10
},
normalLabel: {
fontWeight: "normal",
fontSize: 18,
alignSelf: "center",
color: "black",
marginBottom: 20,
marginTop: 10
}
});
export default RegisterHost;
myTabPage (BankCustomerRegister)
import React, { Component } from "react";
import {
Text,
View,
Image,
StyleSheet,
TextInput,
TouchableOpacity
} from "react-native";
import { TabNavigator } from "react-navigation";
import{StackNavigator} from 'react-navigation';
import FetchData from "../../utils/fetch.js";
class BankCustomerRegister extends Component {
constructor(props) {
super(props);
this.state = {
stCustId: "",
stIdcard: "",
stPhoneNum: "",
isMounted: false
};
}
}
};
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Image source={require("../../resources/icons/bank.png")} />
<TextInput
style={styles.textInput}
onChangeText={this._handleCustId}
placeholderTextColor="black"
placeholder="Customer ID"
/>
<TextInput
style={styles.textInput}
placeholderTextColor="black"
onChangeText={this._handleIdCard}
placeholder="ID card"
/>
<TextInput
style={styles.textInput}
placeholderTextColor="black"
onChangeText={this._handlePhoneNum}
placeholder="Phone No"
/>
<TouchableOpacity
onPress={() => {
// NOTICE HERE
const navigateAction = NavigationActions.navigate({
routeName: "preactivate",
params: {},
});
this.props.navigation.dispatch(navigateAction);
}}
style={styles.touchable}
>
<Text style={styles.btnlabel}>Register</Text>
</TouchableOpacity>
</View>
);
}
}
export default BankCustomerRegister;
when i click touchable.its supposed to navigate to otherPage, i'ts not navigating anywhere , even no errors.
myOtherPage(preactivate)
import React, { Component } from "react";
import {
View,
Text,
StyleSheet
} from "react-native";
class PreUserActivation extends Component {
// Render callBack
render() {
return (
<View style={styles.container}>
<Text>Screen</Text>
</View>
);
}
}
export default PreUserActivation;
My router config in router.js
//tab Router
export const Tab = TabNavigator(
{
BankCustomerRegister: {
screen: BankCustomerRegister,
navigationOptions: {
tabBarLabel: "Bank Customer"
}
},
nonbankcustomer: {
screen: NonCustomerRegister,
navigationOptions: {
tabBarLabel: "New Customer"
}
}
},
{
tabBarPosition: "top",
animationEnabled: true,
tabBarOptions: {
// activeTintColor: "#e91e63",
labelStyle: {
fontSize: 16
},
style: {
backgroundColor: "#08AE9E"
},
tabStyle: { width: 200 } //Set width to make INLINE TABS
}
}
);
export const Root = StackNavigator({
board: {
screen: OnBoardScreen,
navigationOptions: {
header: null
}
},
preactivate: {
screen: PreUserActivation,
navigationOptions: {
header: null
}
},
Tabs: {
screen: Tab
}
});
Is there something i'm missing.
You need a reset. Use navigate() when navigating from one tab to another in the same TabNavigator for example. In your case, you are navigating to a screen in the parent navigator. Try this instead:
onPress={() => {
const navigateAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'preactivate'})
]
});
this.props.navigation.dispatch(navigateAction);
}}