React Native

React Native Login sample using Relay

Introduction

In the last tutorial, you’ve implemented the User Sign Up to your React Native App using Back4App and Relay. In this guide, you’ll build the login mechanism complementing your App auth feature.

As you may know, Parse already provides by default a User class User, which already has a ready-to-use GraphQL Mutation to login in users when it is necessary for your app.

The flow here will be very similar to the User Sign Up tutorial. You’‘ll build a Login screen using formik, then this form will call the Relay Mutation. The Relay Mutation will communicate with the Back4App Server handling the whole process of authentication.

Goal

At the end of this guide, you will have a React Native application with the user login feature implemented, as shown below.

Prerequisites

  • An app created at Back4App using the Parse Server Version 3.10 or above.
  • You have to conclude the Relay Environment setup tutorial;
  • Expect an app with a simple sign in form. Here we are using an Expo app having a Form with the username and password.
  • For this tutorial, we are going to use the Expo as a React Native framework;
  • For this tutorial, we are going to use Javascript as our default implementation language;

Step 1 - Creating Sign In Form

If the application already has a Form component, go to step 2. Otherwise, feel free to follow our boilerplate. The form is similar to the form used in the Sign-Up doc. You can also use it as a basis for login. Please go to User User Sign Up if you want to learn how to implement it. The Login form code should look like this:

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
import React, {useState} from 'react';
import LogInMutation from './mutations/LogInMutation';
import Environment from '../../relay';
import {FormikProvider, useFormik} from 'formik';
import {Button, Text, View} from 'react-native';

import styled from 'styled-components';

const TextInput = styled.TextInput``;

const SignIn = () => {
  const [userLogged, setUserLogged] = useState({});

  const onSubmit = async (values) => {
    // @todo the mutation will be implemented here
  };

  const formikbag = useFormik({
    initialValues: {
      username: '',
      password: '',
    },
    onSubmit,
  });

  const {handleSubmit, setFieldValue} = formikbag;

  if (userLogged?.id) {
    return (
      <View style={ {marginTop: 15, alignItems: 'center'} }>
        <Text>User {userLogged.name} logged</Text>
      </View>
    );
  }

  return (
    <FormikProvider value={formikbag}>
      <View style={ {marginTop: 15, alignItems: 'center'} }>
        <Text>Username</Text>
        <TextInput
          name={'username'}
          style={ {width: 150, height: 30, borderColor: 'gray', borderWidth: 1} }
          autoCapitalize="none"
          onChangeText={(text) => setFieldValue('username', text)}
        />

        <Text style={ {marginTop: 15} }>Password</Text>
        <TextInput
          style={ {width: 150, height: 30, borderColor: 'gray', borderWidth: 1} }
          name={'password'}
          autoCapitalize="none"
          secureTextEntry={true}
          onChangeText={(text) => setFieldValue('password', text)}
        />
        <Button title={'sign in'} onPress={() => handleSubmit()} />
      </View>
    </FormikProvider>
  );
};

export default SignIn;

Run your application, and you’ll see a screen as shown below.

Please, look at the onSubmit function. Note that the Relay Mutation will be inside of this function. Again, it is not a problem if the application is not using Formik. Once you’re implementing a Form Component, the Relay Mutation only needs to be called inside the submit function.

Step 2 - Creating the Mutation

Using the Colocation principle, let’s create a new folder called mutations the most closely to the Form Component. If you want to learn more about colocation please go to Getting Started guide.

In the image below, you can see the colocation principle in practice. Everything related to the component is close to it. A folder wraps the LogIn component, and inside of it, you’ll create another folder called mutations. In the mutations’ folder, you will create the Relay Mutation.

This pattern works perfectly on big projects. Every time you have a new mutation, put it close to the component that will use it.

Inside this folder, you will create a new file called LogInMutation.js. According to our Working with users guide, where we explained the Relay Mutations, you will create a commit function, as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function commit({ environment, input, onCompleted, onError }) {
  const variables = { input };

  commitMutation(environment, {
    mutation,
    variables,
    onCompleted,
    onError,
  });
}

export default {
  commit,
};

Before going back to the form component, let’s create our variable that will receive the GraphQL Fragment, representing the Mutation. The GraphQL Fragment is what the Relay Compiler will read and match with schema.graphql.

Before the commitMutation, copy and paste the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const mutation = graphql`
  mutation LogInMutation($input: LogInInput!) {
    logIn(input: $input) {
      viewer {
        user {
          id
          username
          createdAt
        }
        sessionToken
      }
    }
  }
`;

Final file:

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
import { commitMutation, graphql } from 'react-relay';

const mutation = graphql`
  mutation LogInMutation($input: LogInInput!) {
    logIn(input: $input) {
      viewer {
        user {
          id
          createdAt
          updatedAt
          username
        }
        sessionToken
      }
    }
  }
`;

function commit({ environment, input, onCompleted, onError }) {
  const variables = { input };

  commitMutation(environment, {
    mutation,
    variables,
    onCompleted,
    onError,
  });
}

export default {
  commit,
};

Since the GraphQL Fragment represents the backend, to get the code of Relay Mutation, you can go to the Back4App GraphQL Cookbook and find the Fragment.

Run yarn relay to generate the new mutation and update the files. If everything is okay the types of mutation it will be generated and you can go forward.

Step 3 - Implement On Submit Function

The submit step is the most important. Here is where the Relay Mutation magic happens.

this step gets the values of the form from the formik. If the application is not using formik, the values need to be available here independent of the way they get it.

Back to Form Component, let’s start the implementation of the Relay Mutation.

Import the mutation

1
import LogInMutation from './mutations/LogInMutation';

Inside of OnSubmit function, stars creating the input variables:

1
2
3
4
5
6
7
const onSubmit = (values) => {
    const {username, password} = values;
    const input = {
        username,
        password,
    };
}

The values are injected by Formik. Here, if you are not using formik, the values will likely come via the form’s native oSubmit or as you prefer.

At last, call the Mutation passing all props (remember to import them).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    LogInMutation.commit({
      environment: Environment,
      input,
      onCompleted: (response) => {
        if(!response?.logIn || response?.logIn === null) {
          alert('Error while logging');
          return;
        }

        const { viewer } = response?.logIn;
        const { sessionToken, user } = viewer;

        if (sessionToken !== null) {
          setUserLogged(user);
          alert(`user ${user.username} successfully logged`);
          return;
        }
      },
      onError: (errors) => {
        alert(errors[0].message);
      },
    });

Final result of onSubmit

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
const onSubmit = (values) => {
    const { username, password } = values;
    
    const input = {
      userFields: {
        username,
        password,
      },
    };

    LogInMutation.commit({
      environment: Environment,
      input,
      onCompleted: (response) => {
        if(!response?.logIn || response?.logIn === null) {
          alert('Error while logging');
          return;
        }

        const { viewer } = response?.logIn;
        const { sessionToken, user } = viewer;

        if (sessionToken !== null) {
          setUserLogged(user);
          alert(`user ${user.username} successfully logged`);
          return;
        }
      },
      onError: (errors) => {
        alert(errors[0].message);
      },
    });
};

Run your project, register your User and then check it on Back4App Dashboard. The Mutation will return the values from the server. Once the session token is returned, the application can start to manage it.

Testing using the user created on the last tutorial. If everything works ok, it will be showed an alert like below:

Handling Errors

On commit mutation, the application can handle errors on onError. Always will receive an array of errors. The most common is this array has only one object containing the error message. See the example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "errors": [
    {
      "message": "Invalid username/password.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "logIn"
      ],
      "extensions": {
        "code": 202
      }
    }
  ],
  "data": {
    "logIn": null
  }
}

Based on this example feel free to create your our error handle. By now, if some error is returned we just show it by an alert:

1
2
3
onError: (errors) => {
  alert(errors[0].message);
},

Conclusion

You now have an application with a sign-in feature fully working. In the next guide, you will understand how to log out him using the same approach. You will also use Relay Mutations to call our backend.