Complete Guide to AWS Amplify and React Native
Tags: amplify, reactnative, aws, javascript
Published: 2021-01-22 20:57:24

#### Let’s Build an E-commerce App with ReactNative and Amplify

> Click [here](https://youtu.be/NqYQ0Muwf3c) to see the video walkthrough.

# Table of Content

1. Introduction

2. Setting Up the Project

3. Adding Cognito Authentication

4. Adding AppSync API

5. Adding S3 Storage

6. Retrieving AppSync Data

7. Conclusion

# 01. Introduction

Hello! & Welcome to this complete guide on AWS Amplify and React Native. In this tutorial, we will build a simple e-commerce app. Users can log in/signup to this app. Users can add their own products, and all the users can view those products added by different users. 

Here is a quick demo,

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/w7qjm1ix8bqr6rp863pb.jpg">
  <figcaption>Different Screens in Mobile App</figcaption>
</figure>

Even though this is not a very complex application, this will be a perfect starter project for you. We will use different AWS Services like S3 Storage, AWS AppSync API, and Amazon Cognito Authentication. Don’t worry I will explain these in detail, later.

## Architecture Diagram

The following diagram demonstrates our AWS Architecture Diagram.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/yz3l2dv9wki6uahjakv2.jpg">
  <figcaption>AWS Architecture Diagram for Mobile App</figcaption>
</figure>

AWS Amplify makes it much easier to work with these different services. As always, our backend resources will be created and managed by Amplify. Let me explain what these services will do.

So, Amplify is the heart of our backend environment. Those arrows pointing from Amplify means that we will use Amplify to connect those different resources. Every product will have an image. We will store that image in an [S3](https://aws.amazon.com/s3/) Bucket. Product details will be saved in Amazon [DynamoDB](https://aws.amazon.com/dynamodb/), a NoSQL database provided by AWS. To talk with that Database, we will use a GraphQL API provided by AWS [AppSync](https://aws.amazon.com/appsync/). Amazon Cognito will handle authentication.

Ready to build the app? Let’s get started. 👷🏽‍♂️🧰

## Prerequisites

To avoid any disturbances in the future, make sure you have the following prerequisites installed.

* [Node.js](https://nodejs.org/en/download/) v10.x or later

* [npm](https://docs.npmjs.com/cli/v6/commands/npm-install) v5.x or later

* [Amplify CLI](https://docs.amplify.aws/cli/start/install) (version @4.40.1, what I’m using in the tutorial)

* [Android Studio](https://developer.android.com/studio)

* ReactNatice CLI (version 2.0.1)

# 02. Setting Up the Project

## Installing and Configuring Amplify CLI

Through this tutorial, we will work with AWS Amplify CLI. You can install it by running,

```
npm install -g @aws-amplify/cli@4.39.0
```

Then you need to run `amplify configure`. This will set up your Amplify CLI. There you will set up a new IAM User. You will finish setting up your IAM User, by providing the `accessKeyId` and `secretAccessKey` for your IAM user.

If you are stuck at some point, you can refer to this original guideline on installing Amplify CLI, [https://docs.amplify.aws/cli/start/install](https://docs.amplify.aws/cli/start/install)

## Creating a New ReactNative Application

Hope you have installed and configured Amplify CLI.

To work with ReactNative, you will have to set up the Android development environment. You can refer to this original guide, [https://reactnative.dev/docs/environment-setup](https://reactnative.dev/docs/environment-setup)

Let’s create a new React Native app called AmplifyShop.

```
npx react-native init amplify_shop
```

If you have already installed `react-native-cli`, you can use that instead of `npx`.

Open the newly created React Native Project using Android Studio. Open the Android Emulator using Android Studio’s AVD Manager. In the project directory, run these two commands.

```
npx react-native start 
npx react-native run-android
```

Now, the React Native project should run on your Android Emulator. If you are stuck at some point, please refer to the guide that I have suggested earlier.

## Initializing Amplify Backend

Let’s initialize Amplify for our project. Then we can add services one by one.

In the project directory, run

```
amplify init
```

Then you will be prompted for the following information regarding the project you initialize.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/kzlkqwvh0kz8qehnkjwg.jpg">
  <figcaption>Running 'amplify init'</figcaption>
</figure>

When you initialize your Amplify Project,

* It creates a file called `aws-exports.js` in the src directory. This file will store all the relevant information to identify the AWS resources/services that will allocate in the future.

* It creates a directory called `amplify`. We will use this directory to store the templates and configuration details of the services that we will use in the future. In this directory, Amplify will hold our backend schema as well.

* It creates a Cloud Project. That project can be viewed using the `amplify console` command.

Next, we need to install all the necessary dependencies by running the following command.

```
npm install aws-amplify aws-amplify-react-native amazon-cognito-identity-js @react-native-community/netinfo
```

You will also need to install the pod dependencies for iOS.

```
npx pod-install
```

## Configuring Amplify Backend

To complete setting up our Amplify project, we need to configure amplify in a higher-order component. Adding the following lines of code in your `App.js` or `index.js` file will do the job.

```
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure({
  ...awsconfig,
  Analytics: {
    disabled: true,
  },
});
```

That completes setting up the project. Now let’s add those services one by one.

# 03. Adding Cognito Authentication

Now, adding Authentication to your React Native App never gets easier than Amplify.

## Adding Sign-up and Log-in 

Run `amplify add auth` in your project directory. Submit the following information when configuring Authentication.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/ksng76rmodeldkklvk5i.jpg">
  <figcaption>Running 'amplify add auth'</figcaption>
</figure>

Then, run `amplify push`, to deploy your backend changes. Amplify will take care of the rest by creating your [Cognito Userpool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html).

The authentication UI component, provided by Amplify Framework, will provide the entire authentication flow. 

In the `App.js` file,

* Import `withAuthenticator` component

```
import { withAuthenticator } from 'aws-amplify-react-native'
```

* Wrap the main component with `withAuthenticator` component.

```
export default withAuthenticator(App)
```

When you run your app. This login screen will show up. Try logging in as a new user. This will lead you to the home page. The newly created user will be saved in our Cognito User Pool.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/rj4sw9suaap24rimbdxf.png">
  <figcaption>Sign-in Screen by Amplify</figcaption>
</figure>

Before Adding AppSync API, let’s add navigation to our App. 

## Adding ReactNative Navigation

Our App will contain two screens. One Screen to display the list of products and the other to add new products. Let’s create these two screens. 

Create a new directory called `src`. In that directory, create a folder called `screens`. In that folder `src/screens`, create two new javascript files named `add-product-screen.js` and `home-screen.js` I just prefer this naming convention. You can use any convention.

Copy and paste the following sample code. Do change the function name (‘HomeScreen’ and ‘AddProductScreen’) and the title according to the page.

***directory: src/screens/ home.js, add-product-screen.js***

```
import React from 'react';
import {SafeAreaView, StatusBar, Text} from 'react-native';

const HomeScreen = (props) => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <Text>Home</Text>
      </SafeAreaView>
    </>
  );
};

export default HomeScreen;
```

There are multiple ways to add navigation into ReactNative Apps. In this tutorial, we will use ‘Stack Navigator Library’ from React Navigation. First, we should install it using npm.

```
npm install @react-navigation/native
```

Install all the additional third-party dependencies as well.

```
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view @react-navigation/stack
```

From React Native 0.60 and higher, [linking is automatic](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md). So you don’t need to run `react-native link`.

If you’re on a Mac and developing for iOS, you need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking.

```
npx pod-install ios
```

To finish installing React Native Navigation, add the following import in your `App.js` or `index.js` file.

```
import 'react-native-gesture-handler';
```

For the sake of this tutorial, I will use two additional styling libraries. I will use `react-native-elements` and `react-native-vector-icons`. Let’s install those using npm.

```
npm install react-native-elements
npm install react-native-vector-icons
```

In order to view `fontawesome icons`, we need to add the following line into android/app/**build.gradle** file.

```
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
```

With that out of the way, move into `App.js` file. We will use the `App.js` file to set up navigation in our App. Replace the current code with the following.

***directory: App.js***

```
import React from 'react';
import {StyleSheet, View, TouchableOpacity} from 'react-native';
import {createStackNavigator} from '@react-navigation/stack';
import {NavigationContainer} from '@react-navigation/native';
import AddProductScreen from './src/screens/add-product-screen';
import HomeScreen from './src/screens/home-screen';
import {Button} from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import {withAuthenticator} from 'aws-amplify-react-native';

const App: () => React$Node = () => {
  const Stack = createStackNavigator();
  return (
    <>
      <NavigationContainer>
        <Stack.Navigator initialRouteName="Home">
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            options={({navigation}) => ({
              title: 'Home',
              headerStyle: {
                backgroundColor: '#ff9300',
              },
              headerRight: () => (
                <TouchableOpacity
                  style={styles.addButton}
                  onPress={() => navigation.navigate('AddProduct')}>
                  <Icon name={'plus'} size={20} color="#000000" />
                </TouchableOpacity>
              ),
            })}
          />
          <Stack.Screen
            name="AddProduct"
            buttonStyle={styles.addButton}
            component={AddProductScreen}
            options={{
              title: 'Add Product',
              headerStyle: {
                backgroundColor: '#ff9300',
              },
            }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </>
  );
};
const styles = StyleSheet.create({
  addButton: {
    marginRight: 20,
  },
  logOutBtn: {
    marginLeft: 10,
  },
});

export default withAuthenticator(App);
```

This is the simplest and easiest way to add navigation. We got `Stack.Navigator` Component, which we can provide an initial route. Inside that wrapper component, we can define each screen using the `Stack.Screen` component.

We can use that `options` prop to define the header for each screen. I just added a navigation button on the right side of our header. It should navigate to our AddProduct Screen.

Since we are using Stack Navigation, the new screen gets loaded on top of the previous screen. Therefore, the back button will be added automatically.

## Adding Sign-Out Option

How about adding a sign-out option to our Home Screen. We are already passing `headerRight` to our home screen. We can pass another prop called `headerLeft`. This will create a new button on the left side of our header.

Do paste in the following code along with the import.

```
// importing Auth Class from Amplify Library
import {Auth} from 'aws-amplify';
headerLeft: () => (
  <View style={styles.logOutBtn}>
    <Button
      icon={<Icon name="sign-out" size={25} color="#000000" />}
      onPress={}
      type="clear"
    />
  </View>
),
```

Sign-out button will trigger, `Auth.signOut()` method. This method will end the user’s login session. When the session is over, the login-screen gets loaded automatically.  We don’t need to manage any state variable. Amplify will do the authentication session handling.

So, that’s it for Navigation. Learn more about React Native Navigation [here](https://reactnavigation.org/docs/getting-started). In the end, the result should be something like this.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/cd9axmc2073qvab48tb8.png">
  <figcaption>After Adding Navigation</figcaption>
</figure>

# 04. Adding AppSync API

Let’s store details about products by adding an AppSync API. We will save details about products such as name, price, and description. We will also add an image to every product. Let’s keep that image option for later.

## Executing ‘amplify add api’

As I’ve said earlier, through AppSync, we can build a [GraphQL API](https://graphql.org/). All the heavy lifting, such as connecting and creating DynamoDB tables, generation queries, and mutations, will be done by [AppSync](https://aws.amazon.com/appsync/).

Let’s started by provisioning an AppSync API for our Amplify Backend. Execute,

```
amplify add api
```

and you will be prompted for the following information. 

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/mzh5a7xmrec0lbaswapm.png">
  <figcaption>Running 'amplify add api'</figcaption>
</figure>

Just accept the defaults. 

## Editing GraphQL Schema

Let’s edit our schema. You will find our `schema.graphql` file in `amplify/backend/api/schema.graphql` directory. Copy and paste the following schema.

```
type Product
@model(subscriptions: null)
@auth(
  rules: [
    { allow: owner },
    { allow: private, operations: [read] }
  ]) {
  id: ID!
  name: String!
  description: String
  price: Float
  userId: String
  userName: String
  image: String
}
```

Save the file. Follow with an `amplify push` to deploy your changes into AWS Cloud.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/cxij1fciolzb2pl48gxg.JPG">
  <figcaption>Running 'amplify push' after adding AppSync API</figcaption>
</figure>

Now, our AppSync API has been created. Also, the AppSync Library automatically created queries, mutations for our GraphQL Schema. Run `amplify api console` to view your AppSync API in AWS.

You could play around with some GraphQL operations in this AWS AppSync Console.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/d35btwxr1lox5nrb32xc.JPG">
  <figcaption>AWS AppSync Console</figcaption>
</figure>

## Adding AddProduct Screen

Let’s start interacting with our AppSync API.

Before that, I want to add an extra package that will help in creating a React Native Form. With the [tcomb-form-native](https://www.npmjs.com/package/tcomb-form-native) package, you can quickly create a form on the fly. So, let’s install it using npm.

```
npm install tcomb-form-native
```

Copy and paste the following code into our add-product-screen.js file.

***directory: src/screens/add-product-screen.js***

```
import React, {useState} from 'react';
import {StyleSheet, SafeAreaView, ScrollView} from 'react-native';
import {Button} from 'react-native-elements';
import t from 'tcomb-form-native';
const Form = t.form.Form;
const User = t.struct({
  name: t.String,
  price: t.Number,
  description: t.String,
});
const AddProductScreen = ({navigation}) => {
  const [form, setForm] = useState(null); 
  const [initialValues, setInitialValues] = useState({});
  
  const options = {
    auto: 'placeholders',
    fields: {
      description: {
        multiLine: true,
        stylesheet: {
          ...Form.stylesheet,
          textbox: {
            ...Form.stylesheet.textbox,
            normal: {
              ...Form.stylesheet.textbox.normal,
              height: 100,
              textAlignVertical: 'top',
            },
          },
        },
      },
    },
  };
const handleSubmit = async () => {
    // Saving product details
  };
return (
    <>
      <SafeAreaView style={styles.addProductView}>
        <ScrollView>
          <Form
            ref={(c) => setForm(c)}
            value={initialValues}
            type={User}
            options={options}
          />
          <Button title="Save" onPress={handleSubmit} />
        </ScrollView>
      </SafeAreaView>
    </>
  );
};
const styles = StyleSheet.create({
  addProductView: {
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center',
    paddingTop: 15,
    height: 'auto',
  },
});
export default AddProductScreen;
```

Try running your app, you should see a form just like this.

Let’s inspect our code.

You can see that, I didn't use any `textInputs`. I just defined our fields using `t.struct` and only since the description is a multiple `textInput`, we need to pass in extra options.

In our `handleSubmit` function, we are saving entered details in the database. Paste in the following code inside our `handleSubmit` function. Don’t forget the imports.

```
import { Auth, API, graphqlOperation} from 'aws-amplify';
import {createProduct} from '../../graphql/mutations';

try {
      const value = await form.getValue();
      const user = await Auth.currentAuthenticatedUser();
const response = await API.graphql(
        graphqlOperation(createProduct, {
          input: {
            name: value.name,
            price: value.price.toFixed(2),
            description: value.description,
            userId: user.attributes.sub,
            userName: user.username,
          },
        }),
      );
      console.log('Response :\n');
      console.log(response);
    } catch (e) {
      console.log(e.message);
    }
```

`Auth.currentAuthenticatedUser()` will do exactly what the name suggests. It will return details about the logged-in user. Cognito gives every user an attribute called `sub`, a unique string value. We will save that as the userId assigned to a product. Username will showcase the product owner.

The `createProduct` mutation was generated automatically. Here we are referring to that mutation, which was defined in `graphql/mutations.js` file.

Now, after running the app and saving a product, you should see a console.log of the response. You could also query in the AWS AppSync Console.

# 05. Adding S3 Storage

Now by far, users can save product details. We should also add an extra option to upload a product image. We will need an S3 Bucket to store product images. Working with S3 really gets easier with Amplify. Let me show you.

Before that, install the [React Native image picker library](https://www.npmjs.com/package/react-native-image-picker). 

```
npm install react-native-image-picker
```
    
>    RN >= 0.60
    cd ios && pod install
    
>    RN < 0.60
    react-native link react-native-image-picker

For now, this image picker library will support only 21 or newer SDK Versions. So edit **minSDK version** in ***android/build.gradle*** file.

  >  buildToolsVersion = "29.0.2"
    **minSdkVersion = 21**
    compileSdkVersion = 29
    targetSdkVersion = 29

## Executing ‘amplify add storage’

Run,

```
amplify add storage
```

to create a new S3 Bucket. Accept the defaults in the prompt.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/9lqgpqe3nrltgwyy7oo7.png">
  <figcaption>Running 'amplify add storage'</figcaption>
</figure>

Run `amplify push`, to deploy your changes.

## Updating the Form

Let’s add image uploading and previewing options into our form. I build a fancy ImageUploader Component with an image preview. Make sure to add that by making a new `components` directory in the `src` folder.

***directory: src/components/ImageUploader.js***

```
import React from 'react';
import {View, Image, Button, StyleSheet} from 'react-native';

const ImageUploader = ({handleChoosePhoto, photo}) => {
  return (
    <View style={styles.imageView}>
      {photo && <Image source={{uri: photo.uri}} style={styles.photo} />}
      <Button
        style={styles.photoBtn}
        title="Choose Photo"
        onPress={handleChoosePhoto}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  imageView: {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    paddingBottom: 15,
  },
  photo: {
    width: 200,
    height: 200,
  },
});

export default ImageUploader;
```

In order to use this image uploading option, we will make the following changes in our `add-product-screen.js` file.

We will add the extra ImageUploader Component inside our ScrollView Component.

```
return (
  <>
    <SafeAreaView style={styles.addProductView}>
      <ScrollView>
        <Form
          ref={(c) => setForm(c)}
          value={initialValues}
          type={User}
          options={options}
        />
        <ImageUploader photo={photo} handleChoosePhoto={handleChoosePhoto} />
        <Button title="Save" onPress={handleSubmit} />
      </ScrollView>
    </SafeAreaView>
  </>
);
```

Declare this state variable along with the new handleChoosePhoto function.

```
const [photo, setPhoto] = useState(null);
const handleChoosePhoto = async () => {
  const product = await form.getValue();

  setInitialValues({
    name: product.name,
    price: product.price,
    description: product.description,
  });
  await launchImageLibrary({}, (response) => {
    // console.log(response.data);
    if (response.uri) {
      console.log('Photo Extension: \n');
      // console.log(response);
      setPhoto(response);
    }
  });
};
```

If we don’t set initial values, launching the image library will reset the form.

Don’t forget to add these imports as well.

```
import {launchImageLibrary} from 'react-native-image-picker'; 
import {Storage} from 'aws-amplify';
```

You can do a test run of the form. You should see something like this.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/3g2gwrtxc0hrlkzzjpef.png">
  <figcaption>AddProduct Screen</figcaption>
</figure>

Also, we should update our `handleSubmit` function.

```
const handleSubmit = async () => {
  try {
    const value = await form.getValue();
    console.log('value: ', value);
    const user = await Auth.currentAuthenticatedUser();
    if (photo) {
      const response = await fetch(photo.uri);

      const blob = await response.blob();
      console.log('FileName: \n');
      await Storage.put(photo.fileName, blob, {
        contentType: 'image/jpeg',
      });
    }
    const response = await API.graphql(
      graphqlOperation(createProduct, {
        input: {
          name: value.name,
          price: value.price.toFixed(2),
          description: value.description,
          userId: user.attributes.sub,
          userName: user.username,
          image: photo.fileName,
        },
      }),
    );
    console.log('Response :\n');
    console.log(response);
    navigation.navigate('Home');
  } catch (e) {
    console.log(e.message);
  }
};
```

We can upload an S3 Image into our bucket using `Storage.put` method, provided by AWS Amplify Library. We need our file name (image key in S3 ) to access our file again. So we will store that in our database.

Try uploading a new image. Submit the form. Wait until the image uploads. You should see a console.log like this.

```
    [Sat Jan 02 2021 01:58:21.981]  LOG      Response :

    [Sat Jan 02 2021 01:58:21.982]  LOG      {"data": {"createProduct": {"createdAt": "2021-01-01T20:28:22.382Z", "description": "About Sahan New Product", "id": "f3188508-5ee7-4af4-acf3-3c948f61d868", "image": "6ca2947e-766b-445e-b260-0041502e652a", "name": "Sahan New Product", "price": 200, "updatedAt": "2021-01-01T20:28:22.382Z", "userId": "7d5fa0a3-4d26-4354-8028-7cc597a69447", "userName": "sahan"}}}
```

# 06. Retrieving AppSync Data

Now, let’s show a Product List View on our home screen. For that, I have created two new components,

* ProductCard Component

***directory: src/components/ProductCard.js***

```
import React, {useEffect, useState} from 'react';
import {Text, StyleSheet, View} from 'react-native';
import {Card, Icon, Image} from 'react-native-elements';
import {Storage} from 'aws-amplify';

const ProductCard = ({
  productName,
  productOwner,
  productPrice,
  productImage,
}) => {
  const [imageSource, setImageSource] = useState(null);
  const getImage = async () => {
    try {
      const imageURL = await Storage.get(productImage);
      setImageSource({
        uri: imageURL,
      });
    } catch (e) {
      console.log(e);
    }
  };
  useEffect(() => {
    getImage();
  }, []);
  return (
    <Card containerStyle={styles.cardContainer}>
      <Card.Title style={styles.cardTitle}>{productName}</Card.Title>
      <Card.Divider />
      {imageSource && (
        <Image source={imageSource} style={styles.productImage} />
      )}
      {!imageSource && (
        <View style={styles.altView}>
          <Text>Product Image</Text>
        </View>
      )}
      <Text style={styles.productPrice}>{productPrice}$</Text>
      <View style={styles.ownerTitle}>
        <Icon name="person-pin" />
        <Text style={styles.productOwner}>{productOwner}</Text>
      </View>
    </Card>
  );
};
const styles = StyleSheet.create({
  cardContainer: {
    marginBottom: 20,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,

    elevation: 5,
  },
  productImage: {
    width: 200,
    height: 200,
    alignSelf: 'center',
  },
  productPrice: {
    marginTop: 10,
    marginBottom: 10,
    fontSize: 16,
    fontWeight: 'bold',
  },
  altView: {
    width: 200,
    height: 200,
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center',
  },
  cardTitle: {
    fontSize: 20,
  },
  productOwner: {
    fontSize: 16,
    fontWeight: 'bold',
    alignSelf: 'center',
  },
  ownerTitle: {
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
});
export default ProductCard;
```

* ProductList Component

***directory: src/components/ProductList.js***

```
import React from 'react';
import {View, Text, FlatList, StyleSheet, RefreshControl} from 'react-native';

import ProductCard from './ProductCard';

const ProductList = ({productList, refreshing, onRefresh}) => {
  return (
    <View style={styles.productsView}>
      {productList && (
        <FlatList
          style={styles.productList}
          refreshControl={
            <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
          }
          keyExtractor={(item) => item.id}
          data={productList}
          renderItem={({item}) => {
            return (
              <ProductCard
                productName={item.name}
                productImage={item.image}
                productOwner={item.userName}
                productPrice={item.price}
              />
            );
          }}
        />
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  itemText: {
    fontSize: 15,
  },
  productText: {
    fontSize: 20,
    fontWeight: 'bold',
    alignSelf: 'center',
  },
  productsView: {
    padding: 0,
    margin: 0,
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
  },
  productList: {
    padding: 5,
    marginBottom: 20,
  },
});
export default ProductList;
```

Now, let’s use this ProductList Component in the Home Screen. Replace the current sample code with the following code.

***directory: src/screens/home-screen.js***

```
import React, {useEffect, useState} from 'react';
import {API} from 'aws-amplify';
import {SafeAreaView, StatusBar, TouchableOpacity} from 'react-native';

import {listProducts} from '../../graphql/queries';
import ProductList from '../components/ProductList';
const HomeScreen = (props) => {
  const [productsList, setProducts] = useState([]);
  const [refreshing, setRefreshing] = useState(false);

  const fetchProducts = async () => {
    try {
      const products = await API.graphql({query: listProducts});
      if (products.data.listProducts) {
        console.log('Products: \n');
        console.log(products);
        setProducts(products.data.listProducts.items);
      }
    } catch (e) {
      console.log(e.message);
    }
  };
  useEffect(() => {
    fetchProducts();
  }, []);

  const onRefresh = async () => {
    setRefreshing(true);
    await fetchProducts();
    setRefreshing(false);
  };
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        {productsList && (
          <ProductList
            productList={productsList}
            refreshing={refreshing}
            onRefresh={onRefresh}
          />
        )}
      </SafeAreaView>
    </>
  );
};

export default HomeScreen;
```

In the useEffect hook of our Home Screen, we are fetching all products. This time, we are running a GraphQL query `listProducts`, which will be defined automatically in the `graphql/queries.js` file.

We are passing those fetched products, into our ProductList Component. ProductList Component will render a ProductCard for each product.

In PtoductCard Component, when we pass in the image filename to `Storage.get` function, we will get the full image URL. 

Try running your app, you should now see your product list.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/wtxi2skgh2z54dy63mxz.png">
  <figcaption>Home Screen</figcaption>
</figure>

# 07. Conclusion

With that, we were able to complete all our functionalities successfully. How about letting users order products? I will save that for you to try on 😃.

Congratulations on Completing the Tutorial! 🎉

If we recap on what we have done,

1. We added Cognito Authentication to let users log-in or sign-up to our app.

2. Additionally, we included Navigation and Sign-out options.

3. We created the AppSync GraphQL API, and we saved some product details in our Database.

4. We created S3 Bucket to let users upload an image to each product.

5. At the Home Screen, we were able to show a product ListView to the user.

I think now you have a good understanding of working with these different AWS resources in your ReactNative Apps.

I hope you have completed all the steps without running into any issues. However, if you do, you can ask anything in the comments section below.

Video Walkthrough related to this BlogPost:

***Aws Amplify and React Native Crash Course***

{% youtube NqYQ0Muwf3c %}