Add a loader for authentication flow on React Native Expo - react-native

I just added the loader that is loaded when the user sends the request to login by clicking the button with this code:
import React, { useState } from 'react';
import { Text, TextInput, SafeAreaView, StyleSheet, TouchableOpacity, Image, KeyboardAvoidingView, ActivityIndicator } from 'react-native';
import {AuthContext} from './utils';
// Creating Login Activity.
export function SignInScreen() {
const [isLoading, setLoading] = useState(false);
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const { signIn } = React.useContext(AuthContext);
return (
<KeyboardAvoidingView behavior={Platform.OS == 'ios' ? 'padding' : 'height'} style={styles.keyboard_login}>
<SafeAreaView style={styles.contenitore_login}>
{isLoading == true ? <ActivityIndicator size="large"/> : (
<>
<Image style={styles.logo} source={require('../assets/logo.png')} />
<TextInput
style={styles.campo_login}
placeholder="Email"
value={email}
onChangeText={setEmail}
autoCapitalize='none'
textContentType='emailAddress'
keyboardType='email-address'
/>
<TextInput
style={styles.campo_login}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity style={styles.cta_login} onPress={() => { setLoading(true); signIn({ email, password });}}>
<Text style={styles.testo_cta_login}>Accedi</Text>
</TouchableOpacity>
</>
)}
</SafeAreaView>
</KeyboardAvoidingView>
);
}
Everything works perfectly when the user enters the correct email and password, but when they enter the wrong credentials the loader keeps going.
}).then((response) => response.json()).then((responseJson) => {
// If server response message same as Data Matched
if(responseJson === 'Data Matched'){
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
}else{
Alert.alert(responseJson);
}
}).catch((error) => {
console.error(error);
});
As you can see if the fetch URL says "Data Matched" then it is assigned SIGN_IN which then returns the user to the home of my app, but in the case where the logins don't match I don't know what to do to send the user back to the login page.
What is the navigation code that is used in this case? Thanks!

We can have variable let's say loading
const [loading, setLoading] = useState(false)
And as far I can see your Sigin function connects to db. So do something like
const authContext = React.useMemo(
() => ({
signIn: async data => {
setLoading(true);
// Some code here
setLoading(false);
}
})
)
And then show an Activity indicator or any component you want to show fetching something like this
import React from 'react';
import PropTypes from 'prop-types';
import {View, ActivityIndicator, Modal} from 'react-native';
import styles from './Styles/FullScreenLoaderStyles';
const FullScreenLoader = ({loading}) => (
<Modal transparent={true} animationType={'none'} visible={loading}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator color="#000" animating={loading} />
</View>
</View>
</Modal>
);
FullScreenLoader.propTypes = {
loading: PropTypes.bool,
};
export default FullScreenLoader;
I have updated the loading screen code. Now you can import this in your google signin screen like
import FullScreenLoader from './FullScreenLoader';
// some code
return(
<View>
<FullScreenLoader loading={isLoading} />
</View>
)
FullScreenLoaderStyles.js
import {StyleSheet} from 'react-native';
import colors from '../../Themes/Colors';
export default StyleSheet.create({
modalBackground: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#00000040',
},
activityIndicatorWrapper: {
backgroundColor: colors.grey,
height: 50,
width: 50,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
},
});

Related

AsyncStorage Value Only Getting When Screen Is Refreshed

I am trying to get AsyncStorage on B screen from A screen. On A screen i am saving them and getting them on B, but problem seems to be like that when i navigate to screen b i only get the values when i refresh B screen. It works fine in Expo but in emulator it doesn't.
Please have a look at my code :
import React from 'react';
import { Button, View, Text, TextInput, TouchableOpacity } from 'react-native';
import { styles } from './styles';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import AsyncStorage from '#react-native-async-storage/async-storage';
function HomeScreen({ navigation }) {
const [email, setEmail] = React.useState('');
const redirect = (emailAddress) => {
storeData(emailAddress);
navigation.navigate('Details');
};
const storeData = async (emailAddress) => {
try {
await AsyncStorage.setItem('email', emailAddress);
} catch (e) {
console.log('Error Saving Data', e);
}
};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TextInput
style={styles.input}
value={email}
onChangeText={(email) => setEmail(email)}
placeholder="Enter email address here"
placeholderTextColor="rgba(62, 88, 112, 0.5)"
keyboardType="email-address"
underlineColorAndroid={'transparent'}
autoCapitalize="none"
/>
<TouchableOpacity style={{ margin: 20 }} onPress={() => redirect(email)}>
<Text style={styles.registerText}> Register</Text>
</TouchableOpacity>
</View>
);
}
function DetailsScreen() {
const [text, setText] = React.useState('');
React.useEffect(() => {
const getEmail = async () => {
try {
const email = await AsyncStorage.getItem('email');
if (email !== null) {
setText(email);
}
} catch (e) {
console.log(' error : ' + e);
}
};
getEmail();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen : {text}</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Please help me solve this.
Solution:
To make the details page get the value correctly you need to set the value and wait for it to set in your home page before navigating to details screen so you can get it in the next screen.
const redirect = async (emailAddress) => {
await storeData(emailAddress);
navigation.navigate('Details');
};
Better Solution:
First of all, you don't need to store email in your asyncStorage and retrieve it in the details you can pass it as a route param like this:
navigation.navigate('Details', {email});
and in your details screen:
function DetailsScreen({route}) {
const {email} = route.parmas;
...
}

Conditional rendering not working React Native IOS App

I am a developing an Iphone App using React Native. I want to display table using DataTable. For a User, they have specific devices assigned. I used conditional rendering and getting the table with assigned devices for a user correctly, but when the user has no specific devices I want a message to be displayed as 'No Devices to display'. Below is the code,
ManageDevice.js:
import React, {useEffect} from 'react';
import {View,Text,StyleSheet,ScrollView} from 'react-native';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import HeaderButton from '../../components/UI/HeaderButton';
import {useDispatch, useSelector} from "react-redux";
import {DataTable, TextInput} from 'react-native-paper';
import AsyncStorage from '#react-native-community/async-storage';
import * as authActions from "../../store/actions/auth";
const ManageDevice = props =>{
const dispatch = useDispatch();
const devices = useSelector(state => state?.auth?.availableDevice);
useEffect(() => {
const onScreenLoad = async () => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const {userid} = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
}, [dispatch]);
if (devices)
{
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
<DataTable>
<DataTable.Header>
<DataTable.Title>
<Text>Target Id</Text>
</DataTable.Title>
<DataTable.Title>
<Text>Target Name</Text>
</DataTable.Title>
</DataTable.Header>
{devices?.map((item, key) => (
<DataTable.Row>
<DataTable.Cell>{item.TargetId}</DataTable.Cell>
<DataTable.Cell><TextInput
editable={true}
value={item.TargetName}
theme={{ colors: { placeholder: "#f5f5f5",
background: "transparent",
text: 'green', primary: '#D43790' } }}>
</TextInput>
</DataTable.Cell>
</DataTable.Row>
)
)}
</DataTable>
<View style={styles.textview}>
<Text style={styles.textstyle}>Click on the Target Name to edit</Text>
</View>
</View>
</ScrollView>
)
}
else if(!devices){
return(
<View><Text>No Devices to display</Text></View>
)
}
}
ManageDevice.navigationOptions = navData =>{
return{
headerTitle: 'Manage Devices',
headerTitleStyle:{
color:'white',
},
headerTitleAlign:"left",
headerStyle: {
backgroundColor: '#0437F2',
},
headerLeft: () =>
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
iconName={'chevron-back-outline'}
onPress={() => {
navData.navigation.navigate('Home');
}}
/>
</HeaderButtons>
}
};
const styles = StyleSheet.create({
textview:{
top:'5%'
},
textstyle:{
fontWeight:'bold',
color:'orange'
}
})
export default ManageDevice;
Can anyone tell where I am going wrong? When 'devices' is empty, I want the 'No Devices to display' message. Thanks in Advance
Do it like this:
if(devices && devices?.length > 0){
return(
//Do Something
)
}
else{
return(
<View>
<Text>No Devices to display</Text>
</View>
)
}
Hope this works for you.

TouchableOpacity's onPress is not working

Task.js
export const Task = ({ addTask }) => {
const [focusItem, setFocusItem] = useState(null);
return (
<View style={styles.titleContainer}>
<Text style={styles.title}>What would you like to focus on?</Text>
<View style={styles.container}>
<TextInput
style={{ flex: 1 }}
maxLength={50}
value={focusItem}
onSubmitEditing={()=>({ nativeEvent: { text } }) => setFocusItem(text)}
/>
<RoundedButton
style={styles.addSubject}
size={50}
title="+"
onPress={()=>addTask(focusItem)}
/>
</View>
</View>
);
};
App.js
import React, { useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import {Task} from './src/features/task/Task'
export default function App() {
const [task, setTask] = useState(null);
return (
<View style={styles.container}>
{
task ?
(<Text></Text>):
(<Task addTask = {setTask}/>)
}
<Text>{task}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#425670',
},
});
I have tried to send the data from Task to App component by setting the value from Task. But onPress is not working.
I could see the text has been set successfully while executing the onSubmitEditing, but the onPress is doing nothing. Please help me fix this.
Thanks in advance
You need to change this
onSubmitEditing={()=>({ nativeEvent: { text } }) => setFocusItem(text)}
to
onSubmitEditing={({ nativeEvent: { text } }) => setFocusItem(text)}
You could also refer I want to update the parent according to the change of the child to react native

How to store response token & logout using that token from react native

I am working on an app to log in using api and log out by using the retrieved token.
This is my App.js
import React from 'react';
import AppNavigator from './src/navigations/Navigator';
import * as Font from 'expo-font';
import { Ionicons } from '#expo/vector-icons';
export default class App extends React.Component {
state= {
isReady : false
}
async componentDidMount() {
await Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
});
this.setState({ isReady: true });
}
render() {
return (
<AppNavigator />
);
}
}
This is my navigator.js
import React from 'react';
import {createStackNavigator} from 'react-navigation-stack';
import {createAppContainer} from 'react-navigation';
import Login from '../screens/Login';
import QrScan from '../screens/qrscan';
const AppNavigator = createStackNavigator({
Login:{
screen:Login,
navigationOptions : {
headerShown:false
}
},
QrScan:{
screen:QrScan,
navigationOptions : {
headerTitle:''
}
}
},
);
export default createAppContainer(AppNavigator);
This is login screen login.js
import React from 'react';
import {Button,Text,View,Image,TextInput,SafeAreaView,ImageBackground,Alert} from 'react-native';
export default class Login extends React.Component{
constructor(props) {
super(props)
this.state = {
UserName: '',
UserPassword: ''
}
}
UserLoginFunction = () =>{
const { UserName } = this.state ;
const { UserPassword } = this.state ;
fetch('https://api.idepoz.com/ncl/api/login', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: UserName,
password: UserPassword
})
}).then((response) => response.json())
.then((responseJson) => {
//console.log(responseJson);
if(responseJson)
{
this.props.navigation.navigate({routeName:'QrScan'});
}
else{
Alert.alert(responseJson);
}
}).catch((error) => {
console.error(error);
});
}
render(){
const {navigate} = this.props.navigation
return(
<SafeAreaView style={{flex: 1,
}}>
<ImageBackground
style={{ flex: 1,width: "100%", height: "100%"}}
//We are using online image to set background
source={require('../images/rsz_care.jpg')}
>
<View style={{
flexDirection:"row",
justifyContent: 'center',
alignItems:"center",
marginHorizontal:55,
borderWidth:2,
marginTop:100,
paddingHorizontal:10,
borderColor:"#00716F",
borderRadius:23,
paddingVertical:2
}}>
<TextInput
placeholder="Enter Username"
style={{paddingHorizontal:10}}
onChangeText={UserName => this.setState({UserName})}
/>
</View>
<View style={{
flexDirection:"row",
justifyContent: 'center',
alignItems:"center",
marginHorizontal:55,
borderWidth:2,
marginTop:15,
paddingHorizontal:10,
borderColor:"#00716F",
borderRadius:23,
paddingVertical:2
}}>
<TextInput
placeholder="Enter Password"
style={{paddingHorizontal:10}}
onChangeText={UserPassword => this.setState({UserPassword})}
/>
</View>
<View style={{
justifyContent: 'center',
alignItems:"center",
marginTop:15
}}>
<Button title="Forward" onPress={this.UserLoginFunction} color= "#00716F"/>
</View>
</ImageBackground>
</SafeAreaView>
)
}
}
Th following page is loaded when successfully log in qrscan.js
import React from 'react';
import { Container, Header, Title, Drawer, Content, Footer, FooterTab, Button, Left, Right, Body, Text } from 'native-base';
import { Alert } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons';
import { Ionicons } from '#expo/vector-icons';
import SideBar from './components/SideBar';
export default class QrScan extends React.Component{
closeDrawer = () => {
this.drawer._root.close();
}
openDrawer = () => {
this.drawer._root.open();
}
render()
{
return(
<Drawer
ref={(ref) => { this.drawer = ref; }}
content={<SideBar navigator={this.navigator} closeDrawer={this.closeDrawer}/>}
onClose={() => this.closeDrawer()} >
<Container>
<Header>
<Left>
<Button transparent onPress={this.openDrawer.bind(this)}>
<MaterialIcons name="list" size={40} color="#FFFFFF" />
</Button>
</Left>
<Body>
<Title></Title>
</Body>
<Right>
<Button transparent>
<Ionicons name="search" size={40} color="#FFFFFF" onPress={() => Alert.alert('Search Button pressed')} />
</Button>
</Right>
</Header>
<Content>
<Text>
</Text>
</Content>
</Container>
</Drawer>
);
}
}
The following is the sidebar i am using for drawer
import React from 'react';
import { Text, Alert } from 'react-native';
import { Drawer,Container, Content, Header, Right, Button } from 'native-base';
import { FontAwesome } from '#expo/vector-icons';
export default class SideBar extends React.Component {
render() {
return (
<Container>
<Header>
<Right>
<Button transparent>
<FontAwesome name="close" size={24} color="#FFFFFF" onPress={() => this.props.closeDrawer()} />
</Button>
</Right>
</Header>
<Content>
<Button transparent onPress={}>
<Text style={{fontSize: 24}}>Log Out</Text>
</Button>
</Content>
</Container>
);
}
}
I can log in using the api. I am retrieving token, i have console logged it. But i can't store the token and i am trying to logout from the sidebar using that token. Can anyone help?
Create a global state to store the user information and access it anywhere on the screen.
Global State management:
React Redux
Context API
check out the docs for more information:
https://www.toptal.com/react/react-context-api
https://react-redux.js.org/

focus next input field in functional component react native

I've created a login page where it has 2 input fields (username, password). To generate those input fields i am using "Input" component from nativebase. Now i want to focus password field from username input field when user press next from keyboard. But i am having a hard time finding this solution, can anyone help me out with that? Here is my code:
//packages
import React, { createRef, useContext, useEffect, useState } from 'react';
import { Image, Keyboard, ScrollView, StyleSheet, Text, View } from 'react-native';
import { AuthContext } from './context';
// third pirty packages
import { Button, Item, Input, Root, Spinner } from 'native-base';
import FontAwesome from "react-native-vector-icons/FontAwesome";
import Feather from "react-native-vector-icons/Feather";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
listenOrientationChange as lor,
removeOrientationListener as rol
} from 'react-native-responsive-screen';
//assets and components
import logoImageSmall from './../android/app/src/main/res/drawable/logo_small.png';
export const LoginScreen = ({ route, navigation }) => {
const [site_url, setSiteUrl] = useState(route.params.site_url);
const [database_name, setDatabaseName] = useState(route.params.database_name);
const [institute_name, setInstituteName] = useState(route.params.institute_name);
const [logo_location, setLogoLocation] = useState(route.params.logo_location);
const [student_id, setStudentId] = useState('');
const [password, setPassword] = useState('');
const [showLogo, setShowLogo] = useState(true);
const { state, signInAction } = useContext(AuthContext);
let passwordRef = React.createRef();
useEffect(() => {
lor();
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => {
_keyboardDidShow();
}
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
_keyboardDidHide();
}
);
return () => {
rol();
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
let _keyboardDidHide = () => {
setShowLogo(true);
}
let _keyboardDidShow = () => {
setShowLogo(false);
}
const styles = StyleSheet.create({
............
// my styles
............
});
return (
<Root>
<ScrollView contentContainerStyle={styles.loginForm} keyboardShouldPersistTaps="handled">
<Item rounded style={{ borderColor: "#FF3C48" }}>
<FontAwesome name='user' style={styles.iconStyle} />
<Input
style={{ marginLeft: hp("3%") }}
placeholder='Username'
onChangeText={student_id => setStudentId(student_id)}
returnKeyType={"next"}
blurOnSubmit={false}
onSubmitEditing={el => passwordRef.current = el}
onFocus={() => setShowLogo(false)} />
</Item>
<Item rounded style={{ borderColor: "#FF3C48", marginTop: hp("3%") }}>
<FontAwesome name='unlock' style={styles.iconStyle} />
<Input
ref={passwordRef}
style={{ marginLeft: hp("3%") }}
secureTextEntry={true}
placeholder='Password'
onChangeText={password => setPassword(password)}
returnKeyType={"done"}
onFocus={() => setShowLogo(false)} />
</Item>
<Button full danger
style={{ marginTop: hp("3%") }}
onPress={() => {
// check App.js for further process
signInAction({ student_id, password, site_url, database_name });
}}>
{state.sendingRequest ? (<Spinner color='white' />) : (<Text style={{ color: '#ffffff', fontWeight: 'bold', width: '100%', textAlign: 'center', fontSize: hp("2.8%") }}> LOGIN </Text>)}
</Button>
</ScrollView>
</Root>
)
}
Use this passwordRef.current.focus()