Upload Files Using React Native and Firebase (Part 4)
Tags: webdev, javascript, react, firebase
Published: 2020-01-17 17:11:46

---
title: Upload Files Using React Native and Firebase (Part 4)
published: true
description: Track upload progress and download files
tags: webdev, javascript, react, firebase
cover_image: https://thepracticaldev.s3.amazonaws.com/i/fioz0kgq1ppszym9yeev.png
---

# Overview

In the previous [parts] (https://dev.to/younesh1989/upload-files-using-react-native-and-firebase-part-1-a7h), you learned how to set up a Firebase Storage service and wrote custom rules for your storage bucket. You also learned how to use Image Picker to upload photos locally and use the storage API to save photos to your bucket.

In this last part of the series, I’ll show you the following.


* How to monitor the upload progress of your photo.

* Get the download URL to display the photo from storage.

* Render a progress bar and a skeleton placeholder while waiting to fetch the photo from Firebase Storage.

You can find the full code in [Github] (https://github.com/yhenni1989/jarvis).

Let us jump into it.

# 1. Monitor upload progress

As a reminder, here is the full code we wrote in part 3 of the series in `UploafFile/index.js`.

```react
import React, { useState } from 'react';
import { Button, StatusBar } from 'react-native';

import ImagePicker from 'react-native-image-picker';

import { imagePickerOptions, uploadFileToFireBase } from '../../utils';
import { Container, Picture, Skeleton, ProgressBar } from '../../styles';

const UploadFile = () => {
  const [imageURI, setImageURI] = useState(null);


  const uploadFile = () => {
    ImagePicker.launchImageLibrary(imagePickerOptions, imagePickerResponse => {
      const { didCancel, error } = imagePickerResponse;
      if (didCancel) {
        alert('Post canceled');
      } else if (error) {
        alert('An error occurred: ', error);
      } else {
        setImageURI({ uri: downloadURL });
        Promise.resolve(uploadFileToFireBase(imagePickerResponse));
      }
    });
  };

  return (
    <Container>
      <StatusBar barStyle="dark-content" />
      <Button title="New Post" onPress={uploadFile} color="green" />
      {imageURI && <Picture source={imageURI} />}
    </Container>
  );
};

export default UploadFile;
```

Make the following changes to the `uploadFile` function.

```javascript
const uploadFile = () => {
    ImagePicker.launchImageLibrary(imagePickerOptions, imagePickerResponse => {
      const { didCancel, error } = imagePickerResponse;
      if (didCancel) {
        alert('Post canceled');
      } else if (error) {
        alert('An error occurred: ', error);
      } else {
        /*
          Remove these two lines
          setImageURI({ uri: downloadURL });
          Promise.resolve(uploadFileToFireBase(imagePickerResponse)); 
          Replace them with these two lines instead
        */ 
        const uploadTask = uploadFileToFireBase(imagePickerResponse);
        monitorFileUpload(uploadTask);
      }
    });
  };
```

You no longer need to resolve the promise nor set the local state for the image URI. These two steps will be outsourced to a function called `monitorFileUpload` that you’re going to write shortly.

You’re now saving the results of `uploadFileToFirebase` in a variable called `uploadTask` and passing it as a parameter to `monitorFileUpload`.

Add the following code just on top of the `uploadFile` function.


```javascript
const monitorFileUpload = uploadTask => {
  uploadTask.on('state_changed', snapshot => {
    switch (snapshot.state) {
      case 'running':
        setImageURI(null);
        break;
      case 'success':
        snapshot.ref.getDownloadURL().then(downloadURL => {
          setImageURI({ uri: downloadURL });
         });
         break;
      default:
        break;
    }
  });
};

const uploadFile = () => // ..
```

The above function takes `uploadTask` as the argument and uses an observer method `on('state_changed', callback)` to track state changes.

The observer takes two arguments. The first argument is a string parameter, `'state_changed'`, and the second argument is a callback with a `snapshot` parameter.

You can find more info on tracking upload progress in the Firebase official docs [here] (https://firebase.google.com/docs/storage/web/upload-files).

With a `switch` statement, we check `snapshot.state` for its different cases (i.e., `'running'`, `'success'`) and update our logic accordingly.

In the case of `snapshot.state` returns a success message, we use `snapshot.ref.getDownloadURL()` to get the remote URL of the uploaded file. We then set the local state to that URL.

Time to test the app. Refresh your simulator, and add a new post. After waiting for a while (until the photo is uploaded and the remote URL is created), you should see the photo displayed on the screen.

# 2. Build the progress bar and a skeleton placeholder

As a best practice, you want to show users a progress bar while waiting for the photo to be fetched from storage. To do this, I’ll show you how to leverage the `task.on()` observer function to build a progress bar for your app.

Start by adding the following function in `utils/index.js`.

```javascript
export const uploadProgress = ratio => Math.round(ratio * 100);
```

The above function takes a `ratio` parameter then returns a rounded percentage. 

Add `uploadProgress` to the imports in `UploadFile/index.js`.

```javascript
import {
  imagePickerOptions,
  uploadFileToFireBase,
  uploadProgress,
} from '../../utils';
```

At this point, you need two things.

* Set the value of the upload progress using the local state.

* Toggle the progress bar with the placeholder when the photo is ready for display.

Add the following code for the local state inside the `UploadFile` component.

```react
// Add this
const [upload, setUpload] = useState({
  loading: false,
  progress: 0,
});

const [imageURI, setImageURI] = useState(null);
```

Update `monitorFileUpload` with the following code.

```react
const monitorFileUpload = task => {
  task.on('state_changed', snapshot => {

    // Get the upload progress
    const progress = uploadProgress(
      snapshot.bytesTransferred / snapshot.totalBytes
    );

    switch (snapshot.state) {
      case 'running':
        setImageURI(null);

        // Set upload state to true and save progress into local state
        setUpload({ loading: true, progress });

        break;
      case 'success':
        snapshot.ref.getDownloadURL().then(downloadURL => {
          setImageURI({ uri: downloadURL });

          // Set upload state to false
          setUpload({ loading: false });

        });
        break;
      default:
        break;
    }
  });
};
```

As you see above, we can access the `bytesTransferred` and `totalBytes` through the `snapshot` parameter.

We pass the ratio `snapshot.bytesTransferred / snapshot.totalBytes` to the `uploadProgress` defined in `utils/index.js` to get the percentage of the upload progress.

In case the upload is still running, we set `loading` to `true` and we save `progress` to the local state. When the upload is successful, we set `loading` to `false`.

Add the following code inside the `return()` statement.

```react
return (
  <Container>
    <StatusBar barStyle="dark-content" />
    <Button title="New Post" onPress={uploadFile} color="green" />
    {imageURI && <Picture source={imageURI} />}

    {upload.loading && (
      <>
        <Skeleton />
        <ProgressBar bar={upload.progress} />
      </>
    )}

  </Container>
);
```

Whenever `upload.loading` is true, we display a `Skeleton` component and a `ProgressBar` component (to be defined shortly).

Notice that `ProgressBar` takes the props `bar={upload.progress}` to be used to set the width of the bar.

Let us define the `Skeleton` and `ProgressBar` styled-components. Add the following code in `styles/index.js`.

```react
// ..

export const ProgressBar = styled.View`
  background-color: #039ae5;
  height: 3;
  width: ${props => props.bar}%;
  align-items: flex-start;
`;

export const Skeleton = styled.View`
  height: 300;
  width: 100%;
  background-color: #ebebeb;
`;
```

Notice that the width of `ProgressBar` is rendered dynamically with the bar props you defined earlier.

Import these two new components in `UploadFile/index.js`.

```javascript
import { Container, Picture, Skeleton, ProgressBar } from '../../styles';
```

The full code in `UploadFile/index.js` should look like this.

```react
import React, { useState } from 'react';
import { Button, StatusBar } from 'react-native';

import ImagePicker from 'react-native-image-picker';

import {
  imagePickerOptions,
  uploadFileToFireBase,
  uploadProgress,
} from '../../utils';
import { Container, Picture, Skeleton, ProgressBar } from '../../styles';

const UploadFile = () => {
  const [upload, setUpload] = useState({
    loading: false,
    progress: 0,
  });
  const [imageURI, setImageURI] = useState(null);

  const monitorFileUpload = task => {
    task.on('state_changed', snapshot => {
      const progress = uploadProgress(
        snapshot.bytesTransferred / snapshot.totalBytes
      );
      switch (snapshot.state) {
        case 'running':
          setImageURI(null);
          setUpload({ loading: true, progress });
          break;
        case 'success':
          snapshot.ref.getDownloadURL().then(downloadURL => {
            setImageURI({ uri: downloadURL });
            setUpload({ loading: false });
          });
          break;
        default:
          break;
      }
    });
  };

  const uploadFile = () => {
    ImagePicker.launchImageLibrary(imagePickerOptions, imagePickerResponse => {
      const { didCancel, error } = imagePickerResponse;
      if (didCancel) {
        alert('Post canceled');
      } else if (error) {
        alert('An error occurred: ', error);
      } else {
        const uploadTask = uploadFileToFireBase(imagePickerResponse);
        monitorFileUpload(uploadTask);
      }
    });
  };

  return (
    <Container>
      <StatusBar barStyle="dark-content" />
      <Button title="New Post" onPress={uploadFile} color="green" />
      {imageURI && <Picture source={imageURI} />}
      {upload.loading && (
        <>
          <Skeleton />
          <ProgressBar bar={upload.progress} />
        </>
      )}
    </Container>
  );
};

export default UploadFile;
```

Time to test your app. Launch or refresh your simulator, and add a new photo.

{% youtube xbPU4adoWlU %}

As you can see, a skeleton placeholder with a blue progress bar is shown while the photo is uploaded to storage.

# Conclusion

Congratulations on completing this series of tutorials. 

You learned how to use `react-native-image-picker` to upload photos from your mobile device to Firebase Storage. You then learned how to track the upload progress and display a skeleton placeholder with a progress bar. When the upload is successful, you learned how to fetch the photo from its remote URL and displayed it on the screen.

I hope you enjoyed it. Take care, and see you in the next one.