User LogIn and LogOut for React Native
Introduction
After implementing a component that handles User Registration in Parse in the last guide, you will now learn how to log in and log out users using the same Parse.User
class. You will also learn to install and configure react-navigation
so you can navigate the user through your new screens and components.
The Parse.User.logIn
method stores in your local storage a valid user session, so future calls to methods like currentAsync
will successfully retrieve your User data. On the other hand, logOut
will clear this session from disk and log out of any linked services in your Parse server.
At any time, you can access this project via our GitHub repositories to checkout the styles and complete code.
Prerequisites
To complete this tutorial, you will need:
- A React Native App created and connected to Back4App.
- Complete the previous guide so you can have a better understanding of the Parse.User class.
Goal
To build a User LogIn and LogOut feature using Parse for a React Native App.
Step 1 - Installing dependencies
At some point, every application in React Native will need screen navigation. You will now learn how to install and configure the most used library in React Native for this, react-navigation
.
Go to your project root directory and install the following dependencies:
cd yourApp
yarn add @react-navigation/native react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
If you are developing for iOS, you need to install the pods to complete your app auto-linking:
cd ios
npx pod-install
Note: Linking is automatic for React native
0.60+
to all platforms, so if you are still using an older version of RN, take a look at React Native docs here.
In your app entry file (App.tsx
or App.js
), add this import line at the very top of the file. Now you need to move your components inside the react-navigation
container, which will encapsulate your app inside a NavigationContainer
:
App.tsx
/App.js
1
2
3
4
5
6
7
8
9
import "react-native-gesture-handler";
// Your other imports go here
import { NavigationContainer } from "@react-navigation/native";
const App = () => {
return <NavigationContainer>{/* Your app code */}</NavigationContainer>;
}
export default App;
The core navigation library has different additional navigation modules like a stack, tabs, drawer, others. Stack navigator is the most straightforward one and the one that you will use in this guide. Proceed with the installation:
# This is the navigator that you will use
yarn add @react-navigation/stack
Step 2 - Creating a StackNavigator
Let’s now create and set up a StackNavigator
. This module will manage and handle screen navigation on your app. Since it’s not a goal here to give you a react-navigation
deep understanding, please go to the official docs to know more.
In your App.js
(App.tsx
if you are using TypeScript) file, import and create a StackNavigator
, create a function containing your user registration screen and insert the navigator inside your app NavigationContainer
. Your main file should look like this:
App.tsx
/App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import 'react-native-gesture-handler';
import React from 'react';
import {Image, SafeAreaView, StatusBar, Text, View} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import Parse from 'parse/react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {UserRegistration} from './UserRegistration';
import Styles from './Styles';
// Your Parse initialization configuration goes here
Parse.setAsyncStorage(AsyncStorage);
const PARSE_APPLICATION_ID: string = 'APPLICATION_ID';
const PARSE_HOST_URL: string = 'HOST_URL';
const PARSE_JAVASCRIPT_ID: string = 'JAVASCRIPT_ID';
Parse.initialize(PARSE_APPLICATION_ID, PARSE_JAVASCRIPT_ID);
Parse.serverURL = PARSE_HOST_URL;
// Wrap your old app screen in a separate function, so you can create a screen
// inside the navigator; you can also declare your screens in a separate file,
// export and import here to reduce some clutter
function UserRegistrationScreen() {
return (
<>
<StatusBar />
<SafeAreaView style={Styles.login_container}>
<View style={Styles.login_header}>
<Image
style={Styles.login_header_logo}
source={require('./assets/logo-back4app.png')}
/>
<Text style={Styles.login_header_text}>
<Text style={Styles.login_header_text_bold}>
{'React Native on Back4App - '}
</Text>
{' User registration'}
</Text>
</View>
<UserRegistration />
</SafeAreaView>
</>
);
}
// Create your main navigator here
const Stack = createStackNavigator();
// Add the stack navigator to your NavigationContainer
// and in it you can add all your app screens in the order you need
// them on your stack
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Sign Up" component={UserRegistrationScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
If you run your app now, you will notice the inclusion of a header at the top of the screen containing your screen name:
Step 3 - Creating a login component
Let’s now build the UserLogIn
functional component in your App. Create a new file in your root directory called UserLogIn.js
(UserLogIn.tsx
if you are using TypeScript) and add the input elements using state hooks to manage their data:
UserLogIn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import React, {FC, ReactElement, useState} from 'react';
import {
Image,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import Parse from 'parse/react-native';
import Styles from './Styles';
export const UserLogIn = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => {}}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign in'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
};
UserLogIn.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import React, {FC, ReactElement, useState} from 'react';
import {
Image,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import Parse from 'parse/react-native';
import Styles from './Styles';
export const UserLogIn: FC<{}> = ({}): ReactElement => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => {}}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign in'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
};
You can now implement the function that will call the Parse.User.logIn
method, using state variables:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const doUserLogIn = async function () {
// Note that these values come from state variables that we've declared before
const usernameValue = username;
const passwordValue = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser) => {
// logIn returns the corresponding ParseUser object
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
// To verify that this is in fact the current user, currentAsync can be used
const currentUser = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
return true;
})
.catch((error) => {
// Error can be caused by wrong parameters or lack of Internet connection
Alert.alert('Error!', error.message);
return false;
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const doUserLogIn = async function (): Promise<boolean> {
// Note that these values come from state variables that we've declared before
const usernameValue: string = username;
const passwordValue: string = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser: Parse.User) => {
// logIn returns the corresponding ParseUser object
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
// To verify that this is in fact the current user, currentAsync can be used
const currentUser: Parse.User = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
return true;
})
.catch((error: object) => {
// Error can be caused by wrong parameters or lack of Internet connection
Alert.alert('Error!', error.message);
return false;
});
};
Insert this function inside the UserLogIn
component, just before the return
call, to be called and tested. Remember to update the form’s login button onPress
action to () => doUserLogIn()
and to import Alert
from react-native
. Your component should now look like this:
UserLogIn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import React, {FC, ReactElement, useState} from 'react';
import {
Alert,
Image,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import Parse from 'parse/react-native';
import Styles from './Styles';
export const UserLogIn = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const doUserLogIn = async function () {
// Note that these values come from state variables that we've declared before
const usernameValue = username;
const passwordValue = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser) => {
// logIn returns the corresponding ParseUser object
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
// To verify that this is in fact the current user, currentAsync can be used
const currentUser = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
return true;
})
.catch((error) => {
// Error can be caused by wrong parameters or lack of Internet connection
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => doUserLogIn()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign in'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
};
UserLogIn.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import React, {FC, ReactElement, useState} from 'react';
import {
Alert,
Image,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import Parse from 'parse/react-native';
import Styles from './Styles';
export const UserLogIn: FC<{}> = ({}): ReactElement => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const doUserLogIn = async function (): Promise<boolean> {
// Note that these values come from state variables that we've declared before
const usernameValue: string = username;
const passwordValue: string = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser: Parse.User) => {
// logIn returns the corresponding ParseUser object
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
// To verify that this is in fact the current user, currentAsync can be used
const currentUser: Parse.User = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
return true;
})
.catch((error: object) => {
// Error can be caused by wrong parameters or lack of Internet connection
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => doUserLogIn()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign in'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
};
Step 4 - Creating a login screen
Let’s now create a new screen for your app that will use your UserLogIn
component. Add the new screen as the first (or top) screen of your StackNavigator
as well:
App.tsx
/App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ...
// Add this new screen function below the UserRegistrationScreen
function UserLogInScreen() {
return (
<>
<StatusBar />
<SafeAreaView style={Styles.login_container}>
<View style={Styles.login_header}>
<Image
style={Styles.login_header_logo}
source={require('./assets/logo-back4app.png')}
/>
<Text style={Styles.login_header_text}>
<Text style={Styles.login_header_text_bold}>
{'React Native on Back4App - '}
</Text>
{' User login'}
</Text>
</View>
<UserLogIn />
</SafeAreaView>
</>
);
}
// ...
// Add the screen as the top one at your StackNavigator
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Login" component={UserLogInScreen} />
<Stack.Screen name="Sign Up" component={UserRegistrationScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
// ...
Go ahead and test your screen and component. Notice that your app will now show the user login screen instead of the user registration one, due to their placement on the StackNavigator
screen list. You will see a message like this after signing in:
Step 5 - Creating a Home Screen and handling navigation
After logging in, you will generally want to take the user to your app home screen. Create this screen in your app’s main file:
App.tsx
/App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ...
// Add this new screen function below the UserRegistrationScreen
function HomeScreen() {
return (
<>
<StatusBar />
<SafeAreaView style={Styles.login_container}>
<View style={Styles.login_header}>
<Image
style={Styles.login_header_logo}
source={require('./assets/logo-back4app.png')}
/>
<Text style={Styles.login_header_text}>
<Text style={Styles.login_header_text_bold}>
{'React Native on Back4App - '}
</Text>
{' Home'}
</Text>
</View>
</SafeAreaView>
</>
);
}
// ...
// Add the screen as the bottom one at your StackNavigator
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="UserLogIn" component={UserLogInScreen} />
<Stack.Screen name="UserRegistration" component={UserRegistrationScreen} />
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
// ...
Now you need to use ‘react-navigation’ to navigate the user after he logs in, adding this new code inside the UserLogIn
component:
UserLogIn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ...
// Add this import
import {useNavigation} from '@react-navigation/native';
export const UserLogIn = () => {
// Add this to use useNavigation hook
const navigation = useNavigation();
//...
const doUserLogIn = async function () {
const usernameValue = username;
const passwordValue = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser) => {
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
const currentUser = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
// Add this to navigate your home screen; Navigation.navigate takes
// the user to the screen named after the one passed as parameter
navigation.navigate('Home');
return true;
})
.catch((error) => {
Alert.alert('Error!', error.message);
return false;
});
};
// ...
UserLogIn.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// ...
// Add this import
import {useNavigation} from '@react-navigation/native';
export const UserLogIn: FC<{}> = ({}): ReactElement => {
// Add this to use useNavigation hook
const navigation = useNavigation();
// ...
const doUserLogIn = async function (): Promise<boolean> {
const usernameValue: string = username;
const passwordValue: string = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser: Parse.User) => {
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
const currentUser: Parse.User = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
// Add this to navigate your home screen; Navigation.navigate takes
// the user to the screen named after the one passed as parameter
navigation.navigate('Home');
return true;
})
.catch((error: object) => {
Alert.alert('Error!', error.message);
return false;
});
};
// ...
You will be redirected to your new HomeScreen
after logging in. You can upgrade this screen by adding this HelloUser
component that shows the current user username:
HelloUser.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, {FC, ReactElement, useEffect, useState} from 'react';
import {Text} from 'react-native';
import Parse from 'parse/react-native';
import Styles from './Styles';
export const HelloUser = () => {
// State variable that will hold username value
const [username, setUsername] = useState('');
// useEffect is called after the component is initially rendered and
// after every other render
useEffect(() => {
// Since the async method Parse.User.currentAsync is needed to
// retrieve the current user data, you need to declare an async
// function here and call it afterwards
async function getCurrentUser() {
// This condition ensures that username is updated only if needed
if (username === '') {
const currentUser = await Parse.User.currentAsync();
if (currentUser !== null) {
setUsername(currentUser.getUsername());
}
}
}
getCurrentUser();
}, [username]);
// Note the condition operator here, so the "Hello" text is only
// rendered if there is an username value
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
{username !== '' && <Text>{`Hello ${username}!`}</Text>}
</View>
</View>
);
};
HelloUser.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, {FC, ReactElement, useEffect, useState} from 'react';
import {Text} from 'react-native';
import Parse from 'parse/react-native';
import Styles from './Styles';
export const HelloUser: FC<{}> = ({}): ReactElement => {
// State variable that will hold username value
const [username, setUsername] = useState('');
// useEffect is called after the component is initially rendered and
// after every other render
useEffect(() => {
// Since the async method Parse.User.currentAsync is needed to
// retrieve the current user data, you need to declare an async
// function here and call it afterwards
async function getCurrentUser() {
// This condition ensures that username is updated only if needed
if (username === '') {
const currentUser = await Parse.User.currentAsync();
if (currentUser !== null) {
setUsername(currentUser.getUsername());
}
}
}
getCurrentUser();
}, [username]);
// Note the condition operator here, so the "Hello" text is only
// rendered if there is an username value
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
{username !== '' && <Text>{`Hello ${username}!`}</Text>}
</View>
</View>
);
};
Note: You can take a better look at React’s
useEffect
hooks here.
After calling this component inside your HomeScreen
, your app should look like this:
Step 6 - Creating a logout component
The UserLogOut
component is simpler than the login since the Parse.User.logOut
method takes no arguments and clears the currentUser
data stored locally automatically. Create this component in your app root directory:
UserLogOut.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import React, {FC, ReactElement} from 'react';
import {Alert, Text, TouchableOpacity, View} from 'react-native';
import Parse from 'parse/react-native';
import {useNavigation} from '@react-navigation/native';
import {StackActions} from '@react-navigation/native';
import Styles from './Styles';
export const UserLogOut = () => {
const navigation = useNavigation();
const doUserLogOut = async function () {
return await Parse.User.logOut()
.then(async () => {
// To verify that current user is now empty, currentAsync can be used
const currentUser = await Parse.User.currentAsync();
if (currentUser === null) {
Alert.alert('Success!', 'No user is logged in anymore!');
}
// Navigation dispatch calls a navigation action, and popToTop will take
// the user back to the very first screen of the stack
navigation.dispatch(StackActions.popToTop());
return true;
})
.catch((error) => {
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TouchableOpacity onPress={() => doUserLogOut()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Logout'}</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
};
UserLogOut.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import React, {FC, ReactElement} from 'react';
import {Alert, Text, TouchableOpacity, View} from 'react-native';
import Parse from 'parse/react-native';
import {useNavigation} from '@react-navigation/native';
import {StackActions} from '@react-navigation/native';
import Styles from './Styles';
export const UserLogOut: FC<{}> = ({}): ReactElement => {
const navigation = useNavigation();
const doUserLogOut = async function (): Promise<boolean> {
return await Parse.User.logOut()
.then(async () => {
// To verify that current user is now empty, currentAsync can be used
const currentUser: Parse.User = await Parse.User.currentAsync();
if (currentUser === null) {
Alert.alert('Success!', 'No user is logged in anymore!');
}
// Navigation dispatch calls a navigation action, and popToTop will take
// the user back to the very first screen of the stack
navigation.dispatch(StackActions.popToTop());
return true;
})
.catch((error: object) => {
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TouchableOpacity onPress={() => doUserLogOut()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Logout'}</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
};
Append this new component to HomeScreen
so you can test it after signing in. Your app home screen will look like this now:
If you perform a successful logout, you will see a message like this while being redirected to the UserLogIn
screen, which is the first one in the navigation stack:
Connect your UserRegistration
to a registration button inside the UserLogIn
screen and repeat the same redirection pattern to your app HomeScreen
. Remember to read through react-navigation
’s docs to discover several customization options and control every aspect of your app navigation.
UserLogIn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// ...
export const UserLogIn = () => {
const navigation = useNavigation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const doUserLogIn = async function () {
// Note that these values come from state variables that we've declared before
const usernameValue = username;
const passwordValue = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser) => {
// logIn returns the corresponding ParseUser object
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
// To verify that this is in fact the current user, currentAsync can be used
const currentUser: Parse.User = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
// Navigation.navigate takes the user to the screen named after the one
// passed as parameter
navigation.navigate('Home');
return true;
})
.catch((error) => {
// Error can be caused by wrong parameters or lack of Internet connection
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => doUserLogIn()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign in'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
<>
<TouchableOpacity onPress={() => navigation.navigate('Sign Up')}>
<Text style={Styles.login_footer_text}>
{"Don't have an account? "}
<Text style={Styles.login_footer_link}>{'Sign up'}</Text>
</Text>
</TouchableOpacity>
</>
</View>
);
};
// ...
UserLogIn.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// ...
export const UserLogIn: FC<{}> = ({}): ReactElement => {
const navigation = useNavigation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const doUserLogIn = async function (): Promise<boolean> {
// Note that these values come from state variables that we've declared before
const usernameValue: string = username;
const passwordValue: string = password;
return await Parse.User.logIn(usernameValue, passwordValue)
.then(async (loggedInUser: Parse.User) => {
// logIn returns the corresponding ParseUser object
Alert.alert(
'Success!',
`User ${loggedInUser.get('username')} has successfully signed in!`,
);
// To verify that this is in fact the current user, currentAsync can be used
const currentUser: Parse.User = await Parse.User.currentAsync();
console.log(loggedInUser === currentUser);
// Navigation.navigate takes the user to the screen named after the one
// passed as parameter
navigation.navigate('Home');
return true;
})
.catch((error: object) => {
// Error can be caused by wrong parameters or lack of Internet connection
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => doUserLogIn()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign in'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
<>
<TouchableOpacity onPress={() => navigation.navigate('Sign Up')}>
<Text style={Styles.login_footer_text}>
{"Don't have an account? "}
<Text style={Styles.login_footer_link}>{'Sign up'}</Text>
</Text>
</TouchableOpacity>
</>
</View>
);
};
// ...
UserRegistration.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
export const UserRegistration = () => {
const navigation = useNavigation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const doUserSignUp = async function () {
// Note that these values come from state variables that we've declared before
const usernameValue = username;
const passwordValue = password;
// Since the signUp method returns a Promise, we need to call it using await
return await Parse.User.signUp(usernameValue, passwordValue)
.then((createdUser) => {
// Parse.User.signUp returns the already created ParseUser object if successful
Alert.alert(
'Success!',
`User ${createdUser.get('username')} was successfully created!`,
);
// Navigation.navigate takes the user to the screen named after the one
// passed as parameter
navigation.navigate('Home');
return true;
})
.catch((error) => {
// signUp can fail if any parameter is blank or failed an uniqueness check on the server
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => doUserSignUp()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign Up'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
<>
<TouchableOpacity onPress={() => navigation.navigate('Login')}>
<Text style={Styles.login_footer_text}>
{'Already have an account? '}
<Text style={Styles.login_footer_link}>{'Log In'}</Text>
</Text>
</TouchableOpacity>
</>
</View>
);
};
// ...
UserRegistration.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// ...
export const UserRegistration: FC<{}> = ({}): ReactElement => {
const navigation = useNavigation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const doUserSignUp = async function (): Promise<boolean> {
// Note that these values come from state variables that we've declared before
const usernameValue: string = username;
const passwordValue: string = password;
// Since the signUp method returns a Promise, we need to call it using await
return await Parse.User.signUp(usernameValue, passwordValue)
.then((createdUser: Parse.User) => {
// Parse.User.signUp returns the already created ParseUser object if successful
Alert.alert(
'Success!',
`User ${createdUser.get('username')} was successfully created!`,
);
// Navigation.navigate takes the user to the screen named after the one
// passed as parameter
navigation.navigate('Home');
return true;
})
.catch((error: object) => {
// signUp can fail if any parameter is blank or failed an uniqueness check on the server
Alert.alert('Error!', error.message);
return false;
});
};
return (
<View style={Styles.login_wrapper}>
<View style={Styles.form}>
<TextInput
style={Styles.form_input}
value={username}
placeholder={'Username'}
onChangeText={(text) => setUsername(text)}
autoCapitalize={'none'}
keyboardType={'email-address'}
/>
<TextInput
style={Styles.form_input}
value={password}
placeholder={'Password'}
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => doUserSignUp()}>
<View style={Styles.button}>
<Text style={Styles.button_label}>{'Sign Up'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={Styles.login_social}>
<View style={Styles.login_social_separator}>
<View style={Styles.login_social_separator_line} />
<Text style={Styles.login_social_separator_text}>{'or'}</Text>
<View style={Styles.login_social_separator_line} />
</View>
<View style={Styles.login_social_buttons}>
<TouchableOpacity>
<View
style={[
Styles.login_social_button,
Styles.login_social_facebook,
]}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-facebook.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-google.png')}
/>
</View>
</TouchableOpacity>
<TouchableOpacity>
<View style={Styles.login_social_button}>
<Image
style={Styles.login_social_icon}
source={require('./assets/icon-apple.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
<>
<TouchableOpacity onPress={() => navigation.navigate('Login')}>
<Text style={Styles.login_footer_text}>
{'Already have an account? '}
<Text style={Styles.login_footer_link}>{'Log In'}</Text>
</Text>
</TouchableOpacity>
</>
</View>
);
};
// ...
Your login and registration screens now should look like these:
Conclusion
At the end of this guide, you learned how to log in and log out Parse users on React Native and to navigate users through your application using react-navigation
. In the next guide, we will show you how to perform useful user queries.