React Navigation animation from Drawer to Stack - react-navigation

I have some Drawer navigation
<Drawer.Navigator
screenOptions={
drawerStatus ? screenOptions : {...screenOptions, swipeEnabled: false}
}
drawerContent={props => <CustomDrawer {...props} />}
initialRouteName={'Main'}>
{screens.map(screen => (
<Drawer.Screen
key={screen.name}
name={screen.name}
component={screen.component}
/>
))}
</Drawer.Navigator>
And in this Drawer I have a nested screen "Post Detail"
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name={'PostDetail'} component={PostDetail} />
</Stack.Navigator>
If I navigate from Drawer to nested StackScreen or go back to Drawer, I don`t catch animation transition.

Apparently, using only the 'createDrawerNavigator', it is not possible to change the screen transition animation. For this, you must create a 'createStackNavigator', inserting your DrawerNavigator in your 'Home'. Do like:
import React from 'react';
import Home from '../pages/home/home';
import HeaderHome from '../shared/headerHome';
import Configuracoes from '../pages/configuracoes';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createStackNavigator } from '#react-navigation/stack';
import { DrawerContent } from '../shared/drawerContent';
const Drawer = createDrawerNavigator();
const Content = (navigation: any) => (
<Drawer.Navigator
drawerContent={props => <DrawerContent {...props} />}
>
<Drawer.Screen name="HomeDrawer" component={Home}
options={{
header: (props) => <HeaderHome value={props} /> //My cystom Header
}}
/>
</Drawer.Navigator>
);
const Stack = createStackNavigator();
const MyStack: React.FC = () => {
return (
<Stack.Navigator
screenOptions={({ route, navigation }) => ({
headerMode: "float",
animationEnabled: true
})}
>
<Stack.Screen name="Home" component={Content} //The name used cannot be identical to the one used in Drawer.Navigator
options={{ headerShown: false }}
/>
<Stack.Screen name="Configuracoes" component={Configuracoes}
options={{
title: "Title page",
headerStyle: {
backgroundColor: "red",
},
headerTintColor: "#fff"
}}
/>
</Stack.Navigator>
);
}
export default MyStack;

Related

Getting undeifined value when passing data from one screen to another screen in react-native

When I am trying to pass data from Login screen to MyProfile screen then I got undefined value.
I am confused that why I am getting undefined value ?
Here is code of my main navigation files.
route.js
import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer, getFocusedRouteNameFromRoute } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { createDrawerNavigator } from "#react-navigation/drawer";
import LoginScreen from "./../screens/Login/index.js";
import MyProfileScreen from "../screens/MyProfile/index.js";
import AboutUsScreen from "../screens/AboutUs/index.js";
import SettingScreen from "../screens/Setting/index.js";
import { Image, StyleSheet, View, TouchableOpacity } from "react-native";
import { heightPercentageToDP as hp, widthPercentageToDP as wp } from 'react-native-responsive-screen';
import { RFValue } from "react-native-responsive-fontsize"
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
const NavigationDrawerStructure = (props) => {
const toggleDrawer = () => {
props.navigationProps.toggleDrawer();
}
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity onPress={() => toggleDrawer()}>
<Image
source={{ uri: 'https://raw.githubusercontent.com/AboutReact/sampleresource/master/drawerWhite.png' }}
style={{
width: 25,
height: 25,
marginLeft: 5
}}
/>
</TouchableOpacity>
</View>
)
}
const geHeaderTitle = (route) => {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'MyProfileScreen';
switch (routeName) {
case 'MyProfileScreen':
return 'Profile';
case 'AboutUsScreen':
return 'AboutUs';
case 'SettingScreen':
return 'Setting';
}
}
const BottomTab = () => {
return (
<Tab.Navigator initialRouteName="MyProfileScreen"
tabBarOptions={{
activeTintColor: "red",
labelStyle: {
fontSize: RFValue('14'),
marginTop: 5
},
style: { height: hp('11') }
}}
>
<Tab.Screen
name="MayProfileScreen"
component={MyProfileScreen}
options={{
tabBarLabel: 'Profile',
tabBarIcon: ({ focused }) => (
focused ?
<Image source={require('./../../asstes/images/profile.png')} style={styles.activeImg} /> :
<Image source={require('./../../asstes/images/profile.png')} style={styles.deActiveImg} />
)
}}
/>
<Tab.Screen
name="AboutUsScreen"
component={AboutUsScreen}
options={{
tabBarLabel: 'AboutUs',
tabBarIcon: ({ focused }) => (
focused ?
<Image source={require('./../../asstes/images/aboutus.png')} style={styles.activeImg} /> :
<Image source={require('./../../asstes/images/aboutus.png')} style={styles.deActiveImg} />
)
}}
/>
<Tab.Screen
name="SettingScreen"
component={SettingScreen}
options={{
tabBarLabel: 'Setting',
tabBarIcon: ({ focused }) => (
focused ?
<Image source={require('./../../asstes/images/setting.png')} style={styles.activeImg} /> :
<Image source={require('./../../asstes/images/setting.png')} style={styles.deActiveImg} />
)
}}
/>
</Tab.Navigator>
)
}
const HomeStack = ({ navigation }) => {
return (
<Stack.Navigator initialRouteName="LoginScreen">
<Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
<Stack.Screen name="MyProfileScreen" component={BottomTab}
options={({ route }) => ({
headerTitle: geHeaderTitle(route),
headerLeft: () => (
<NavigationDrawerStructure navigationProps={navigation} />
),
title: 'Profile',
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
})}
/>
<Stack.Screen name="AboutUsScreen" component={AboutUsScreen}
options={{
title: 'AboutUS',
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}} />
<Stack.Screen name="SettingScreen" component={SettingScreen}
options={{
title: 'Setting',
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}}
/>
</Stack.Navigator>
)
}
const AboutUsStack = ({ navigation }) => {
return (
<Stack.Navigator initialRouteName="AboutUsScreen"
screenOptions={{
headerLeft: () => (
<NavigationDrawerStructure navigationProps={navigation} />
),
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}}
>
<Stack.Screen name="AboutUsScreen" component={AboutUsScreen} options={{ title: 'AboutUs' }} />
</Stack.Navigator>
)
}
const SettingStack = ({ navigation }) => {
return (
<Stack.Navigator initialRouteName="SettingScreen"
screenOptions={{
headerLeft: () => (
<NavigationDrawerStructure navigationProps={navigation} />
),
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}}
>
<Stack.Screen name="SettingScreen" component={SettingScreen} options={{ title: 'Setting', }} />
</Stack.Navigator>
)
}
const Navigation = () => {
return (
<NavigationContainer>
<Drawer.Navigator
drawerContentOptions={{
activeTintColor: '#e91e63',
itemStyle: { marginVertical: 5 }
}}
>
<Drawer.Screen name="HomeStack" options={{ drawerLabel: 'Profile' }} component={HomeStack} />
<Drawer.Screen name="AboutUsStack" component={AboutUsStack} options={{ drawerLabel: 'AboutUs' }} />
<Drawer.Screen name="SettingStack" component={SettingStack} options={{ drawerLabel: 'Setting' }} />
</Drawer.Navigator>
</NavigationContainer>
)
}
const styles = StyleSheet.create({
activeImg: {
height: hp('4.8'), width: wp('8.5'), marginTop: 10, borderRadius: 12, tintColor: 'red'
},
deActiveImg: {
height: hp('4.8'), width: wp('8.5'), marginTop: 10, borderRadius: 12, tintColor: 'gray'
}
})
export default Navigation;
I am calling below function when user click on Login button.
Here is some lines of code that how I am trying to pass data from Login screen to MyProfile screen
Login screen
const resetTextInput = () => {
setName(null);
setPassword(null);
navigation.navigate('MyProfileScreen', { userName: name, userPwd: password,});
}
<TouchableOpacity style={styles.loginBtn} onPress={() => { resetTextInput() }}>
<Text style={styles.loginBtnTxt}>Login</Text>
</TouchableOpacity>
Here is some lines of code that how I am trying to get data from Login screen to MyProfile screen.
MyProfle screen
useEffect(() => {
console.log("username is-->",JSON.stringify(route?.params?.userName));
console.log("userpassword is-->",JSON.stringify(route?.params?.userPwd));
});
Perhaps pass route.params? as an argument inside your useEffect?
useEffect(() => {
...
},[route.params?]);
That way it'll apply the effect when you navigate into it.
Do you have a multiple stack navigator? If you are at different stack navigator and you want to pass params to a screen from different stack navigator, you have to specify the screen.
For example you are at the AuthStack and you want to navigate to MyProfileScreen from the ProfileStack:
navigation.navigate('ProfileStack', { screen: 'MyProfileScreen', params: { //data here } });
if not, I think you got a typo in your BottomTab component. Change the name of the first tab from "MayProfileScreen" to "MyProfileScreen".
then use this to navigate:
navigation.navigate('MyProfileScreen', { //params here });

React navigation custom header doesn't disappear when navigating away from screen (iOS only)

I have a stack navigator where one of the screen uses a custom header:
import { createStackNavigator } from "#react-navigation/stack";
import * as React from "react";
import { Button, View } from "react-native";
const Stack = createStackNavigator();
function ScreenA({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: "center"}}>
<Button title="Click me" onPress={() => navigation.navigate("ScreenB")} />
</View>
);
}
function ScreenB({ navigation }) {
return (
<View style={{ flex: 1 , justifyContent: "center"}}>
<Button title="Click me" onPress={() => navigation.navigate("ScreenA")} />
</View>
);
}
function TestComp() {
return (
<Stack.Navigator>
<Stack.Screen
name="ScreenA"
component={ScreenA}
options={{ header: () => <View style={{ height: 160, backgroundColor: "red" }}></View> }}
/>
<Stack.Screen name="ScreenB" component={ScreenB} />
</Stack.Navigator>
);
}
export default TestComp;
As a result, the header of ScreenA (a red bar) is visible from ScreenB. This doesn't happen on Android where the header is properly shown ONLY on ScreenA.
How can I stop the header from ScreenA from showing on ScreenB?
Solved it by using <Stack.Navigator headerMode="screen"> !

React Native Stack Navigator passing props

I want to pass some props (value) to another page and I'm using stackNavigator
App.js:
import Insert from "./components/pages/Insert";
import ViewData from "./components/pages/ViewData";
const Stack = createStackNavigator();
NavigationContainer>
<Stack.Navigator headerMode={false}>
<Stack.Screen name="Insert Your data" component={Insert} />
<Stack.Screen name="ViewData" component={ViewData} />
</Stack.Navigator>
</NavigationContainer>
Insert.js
const Insert = ({ props, navigation: { navigate } }) => {
const [enteredName, setEnteredName] = useState();
const [enteredSurname, setEnteredSurname] = useState();
const sendValues = (enteredName, enteredSurname) => {
setEnteredName(enteredName);
setEnteredSurname(enteredSurname);
navigate("ViewData", {
name: enteredSurname,
surname: enteredSurname
});
};
...
<View>
<Button
title="Submit"
onPress={() => sendValues(enteredName, enteredSurname)}
/>
ViewData.js
const ViewData = ({props, navigation: { goBack } }) => {
let name = enteredName;
console.log(name); /// I always get undefined
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Here {name}</Text>
<Button onPress={() => goBack()} title="Edit Data" />
</View>
);
};
When I Submit I'm always getting undefined into the console.
For sure I'm mistaking somewhere.
Any idea?
Thanks!!
Refer to https://reactnavigation.org/docs/hello-react-navigation/#passing-additional-props
Use a render callback for the screen instead of specifying a component
prop:
<Stack.Screen name="Home">
{props => <HomeScreen {...props} extraData={someData} />}
</Stack.Screen>
You can change like this
import Insert from "./components/pages/Insert";
import ViewData from "./components/pages/ViewData";
const Stack = createStackNavigator();
<NavigationContainer>
<Stack.Navigator headerMode={false}>
<Stack.Screen name="Insert Your data">
{props => (<Insert {...props} extraData={data}/>)}
<Stack.Screen name="ViewData" component={ViewData} />
</Stack.Navigator>
</NavigationContainer>
Reading your code it seems that you want to pass some params to the render component when navigate.
Refer to https://reactnavigation.org/docs/route-prop , the params from
navigation.navigate(routeName, params)
are passed to the screen render component as propriety of the route object. So in your case it should work:
let name = props.route.params.name;

Multiple drawers in react-navigation 5.0

I wrote below code to make multiple side menus but it occurred error
Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container.
However I've tried to find the Container for multiple drawers but no luck.
What should I do?
Thanks in advance.
import React from 'react';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import { Ionicons } from '#expo/vector-icons';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationNativeContainer } from '#react-navigation/native';
import { Container, Text, Button } from 'native-base';
import 'react-native-gesture-handler'
function BlankScreen({ navigation }) {
return (
<Text>Blank</Text>
);
}
function HomeScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-reverse' }}>
<Button onPress={() => navigation.navigate('Menu')}>
<Text>Go to Menu</Text>
</Button>
<Button onPress={() => navigation.navigate('Favorit')}>
<Text>Go to Favorit</Text>
</Button>
</Container>
);
}
function MenuScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-reverse' }}>
<Button onPress={() => navigation.goBack()}>
<Text>Go back home</Text>
</Button>
</Container>
);
}
function FavoritScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-inverse' }}>
<Button onPress={() => navigation.goBack()}>
<Text>Go back home</Text>
</Button>
</Container>
);
}
const DrawerL = createDrawerNavigator();
const DrawerR = createDrawerNavigator();
export default function App() {
return (
<Container>
<NavigationNativeContainer>
<DrawerL.Navigator initialRouteName="Home" drawerPosition="left">
<DrawerL.Screen name="Home" component={HomeScreen} />
<DrawerL.Screen name="Menu" component={MenuScreen} />
<DrawerL.Screen name="Favorit" component={FavoritScreen} />
</DrawerL.Navigator>
<DrawerR.Navigator initialRouteName="Blank" drawerPosition="right">
<DrawerR.Screen name="Blank" component={BlankScreen} />
<DrawerR.Screen name="Menu" component={MenuScreen} />
<DrawerR.Screen name="Favorit" component={FavoritScreen} />
</DrawerR.Navigator>
</NavigationNativeContainer>
</Container>
);
The way you wrote it won't work, because consider this: you've 2 drawers, one has initial route as "Home", and other as "Blank". Which one should be rendered?
Here, you need to follow the same approach as React Navigation 4, put one drawer inside another:
function HomeStack() {
return (
<Stack.Navigator screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Menu" component={MenuScreen} />
<Stack.Screen name="Favorit" component={FavoritScreen} />
</Stack.Navigator>
)
}
function RightDrawer() {
return (
<DrawerR.Navigator initialRouteName="Home" drawerPosition="right">
<DrawerR.Screen name="Home" component={HomeStack} />
<DrawerR.Screen name="Menu" component={MenuScreen} />
<DrawerR.Screen name="Favorit" component={FavoritScreen} />
</DrawerR.Navigator>
)
}
function LeftDrawer() {
return (
<DrawerL.Navigator initialRouteName="RightDrawer" drawerPosition="left">
<DrawerL.Screen name="RightDrawer" component={RightDrawer} />
</DrawerL.Navigator>
);
}
function App() {
return (
<NavigationNativeContainer>
<LeftDrawer />
</NavigationNativeContainer>
)
}
Since your left drawer won't show the screens that you have in Stack, you will need to provide a custom component for your drawer which lists the screens: https://reactnavigation.org/docs/en/next/drawer-navigator.html#providing-a-custom-drawercontent

How to validate in react-native by using formik and yup?

How do i show error messages here by using formik and yup?
Suppose i want to show an error message for Customer name.
How to do this?
import React, { Component } from 'react';
import { Text,Alert, TextInput, View, StyleSheet, KeyboardAvoidingView, ActivityIndicator, TouchableOpacity, Image, Animated, Easing,} from 'react-native';
import { Button } from 'react-native-elements'
import PropTypes from 'prop-types';
import Dimensions from 'Dimensions';
import { Router, Scene, Actions } from 'react-native-router-flux';
import * as Yup from 'yup';
import { Formik } from 'formik';
import eyeImg from '../images/eye_black.png';
const DEVICE_WIDTH = Dimensions.get('window').width;
const DEVICE_HEIGHT = Dimensions.get('window').height;
I have declared initialValues also.
Please help me.
const initialValues = {
customer_name: "",
mobile: "",
password: "",
repassword: "",
email_address: "",
company_id: "",
profile_image: "",
licence_number: "",
user_status: "Active",
};
Here are my error messages.
const customervalidation = Yup.object().shape({
customer_name: Yup.string().required("Please enter name"),
email_address: Yup.string()
.required("Please enter email address")
.email('Please enter a valid email'),
mobile: Yup.string().required("Please enter mobile"),
password: Yup.string().required("Please enter password"),
repassword: Yup.string().oneOf([Yup.ref('password'), null], 'Passwords must match')
});
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
customer_name: '',
mobile: '',
password: '',
cpassword: '',
email_address: '',
showPass: true,
showConfPass: true,
press: false,
};
this.showPass = this.showPass.bind(this);
this.showConfPass = this.showConfPass.bind(this);
this._onPress = this._onPress.bind(this);
}
showPass() {
//Alert.alert('Credentials', `${this.state.press}`);
this.state.press === false
? this.setState({showPass: false, press: true})
: this.setState({showPass: true, press: false});
}
showConfPass() {
this.state.press === false
? this.setState({showConfPass: false, press: true})
: this.setState({showConfPass: true, press: false});
}
This is actually my API for sign up section.
onSignup() {
const { customer_name, mobile, password, cpassword, email_address } = this.state;
Alert.alert('Credentials', `${customer_name} + ${mobile} + ${password} + ${cpassword} + ${email_address}`);
fetch('url', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer_name: this.state.customer_name,
mobile: this.state.mobile,
email_address: this.state.email_address,
password: this.state.password,
})
}).then((response) => response.json())
.then((responseJson) => {
alert('Success');
}).catch((error) => {
alert('Error');
});
}
_onPress() {
if (this.state.isLoading) return;
this.setState({isLoading: true});
Animated.timing(this.buttonAnimated, {
toValue: 1,
duration: 200,
easing: Easing.linear,
}).start();
setTimeout(() => {
this._onGrow();
}, 2000);
setTimeout(() => {
Actions.forgotpwdScree();
this.setState({isLoading: false});
this.buttonAnimated.setValue(0);
this.growAnimated.setValue(0);
}, 2300);
}
I have added formik here . I want to show error messages during setfieldtouch,onblur and form submit
render() {
return (
<Formik initialValues= {initialValues} validationSchema={customervalidation}>
{({ values, errors, isValid, touched, setFieldTouched, isSubmitting }) => {
return(
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<View style={styles.inputcontainer}>
<Text style={styles.textlabel}>NAME</Text>
<TextInput
value={this.state.customer_name}
onChangeText={(customer_name) => this.setState({ customer_name })}
placeholder={'Name'}
style={styles.input}
/>
</View>
<View style={styles.inputcontainer}>
<Text style={styles.textlabel}>PHONE NUMBER</Text>
<TextInput
value={this.state.mobile}
onChangeText={(mobile) => this.setState({ mobile })}
placeholder={'Mobile Number'}
style={styles.input}
/>
</View>
<View style={styles.inputcontainer}>
<Text style={styles.textlabel}>PASSWORD</Text>
<TextInput
value={this.state.password}
secureTextEntry={this.state.showPass}
onChangeText={(password) => this.setState({ password })}
placeholder={'PASSWORD'}
returnKeyType={'done'}
autoCapitalize={'none'}
autoCorrect={false}
style={styles.input}
/>
</View>
<TouchableOpacity
activeOpacity={0.7}
style={styles.btnEye}
onPress={this.showPass}>
<Image source={eyeImg} style={styles.iconEye} />
</TouchableOpacity>
<View style={styles.inputcontainer}>
<Text style={styles.textlabel}>CONFIRM PASSWORD</Text>
<TextInput
value={this.state.cpassword}
secureTextEntry={this.state.showConfPass}
onChangeText={(cpassword) => this.setState({ cpassword })}
placeholder={'CONFIRM PASSWORD'}
returnKeyType={'done'}
autoCapitalize={'none'}
autoCorrect={false}
style={styles.input}
/>
</View>
<TouchableOpacity
activeOpacity={0.7}
style={styles.btnEye2}
onPress={this.showConfPass}>
<Image source={eyeImg} style={styles.iconEye} />
</TouchableOpacity>
<View style={styles.inputcontainer}>
<Text style={styles.textlabel}>EMAIL ID</Text>
<TextInput
value={this.state.email_address}
onChangeText={(email_address) => this.setState({ email_address })}
placeholder={'Email Address'}
style={styles.input}
/>
</View>
<View style={styles.inputcontainerB}>
<Text style={styles.textR} >I AGREE WITH UP TERMS</Text>
<Button
large
title='SIGN UP'
icon={{name: 'lock', type: 'font-awesome'}}
onPress={this.onSignup.bind(this)}
/>
</View>
</KeyboardAvoidingView>
);
}}
</Formik>
);
}
}
You can use: values, handleSubmit, handleChange, errors, touched, handleBlur, from the render prop in formik component, Formik lib already does the updates to the form values, so there is no need to use state for this, for example, for the customer_name you need to add a Text component to show the error.
<Formik
initialValues={initialValues}
onSubmit={this.onSignup}
validationSchema={customervalidation}
render={({
values,
handleSubmit,
handleChange,
errors,
touched,
handleBlur
}) => (
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<View style={styles.inputcontainer}>
<Text style={styles.textlabel}>NAME</Text>
<TextInput
value={values.customer_name}
onBlur={handleBlur('customer_name')}
onChangeText={handleChange('customer_name')}
placeholder={'Name'}
style={styles.input}
/>
<Text>{touched.customer_name && errors.customer_name}</Text>
</View>
...
<Button
large
title='SIGN UP'
icon={{name: 'lock', type: 'font-awesome'}}
onPress={handleSubmit}
/>
</KeyboardAvoidingView>
)
/>
the handleSubmit prop will pass the function declared in the onSubmit prop to the render, which will send the param values, which in your case will have the updated values declared in initialValues
{
customer_name: "",
mobile: "",
password: "",
repassword: "",
email_address: "",
company_id: "",
profile_image: "",
licence_number: "",
user_status: "Active"
}

Resources