Build Offline Capable Apps With React Native and Redux Offline

A guide for building a React Native image upload app with offline capabilities.

Matthew Brown
Nov 5 · 8 min read
Image for post
Image for post
Made by the author in Canva

A common requirement for mobile apps is offline capability. If you expect your users to use the app in an area with limited connectivity then having offline capabilities is a must.

For example, the startup I work for built an app for collecting crop data from the fields. Typically the fields are in a pretty area and a good network connection is not always guaranteed. Therefore we need the ability for users to do their work out in the field and then sync their data with the server once they have returned to an area with better network connectivity.

In this article, I’m going to demonstrate how to build a basic image upload app with offline capabilities. We will be using React Native and Redux offline in our demonstration app. We will also be building a NodeJS API for accepting the data from our app and storing it in a database. If you need an introduction to building React Native apps be sure to check out my article below.

What are we building?

In this article we will be building the following:

  • A local instance of PostgreSQL for storing data
  • NodeJS API with Sequelize for communicating with the database
  • A React Native app for capturing and uploading images with offline capabilities

Here is a preview of the end result:

Image for post
Image for post
Screenshot by the author

Prerequisites

Here’s what you’ll need to follow along with this example:

Offline Capabilities

The easiest path for building an app with offline capabilities is to build the app from the beginning with offline in mind. It’s possible to add to an existing app, but it is much easier to include from the start if it’s something you know will be needed.

You can definitely build your own logic and services for handling everything around offline mode, but this can be quite complex to implement. In your app, you would need to keep track of the connection status of the user’s device and handle it accordingly. When the user does something that would cause an update would have to check that status and either send it to the server or store locally until it can be synced at a later time.

In this article, we will be using a library called Redux Offline. This will look very familiar if you’ve ever built an app with Redux before as it works the same. We provide Redux Offline with the information for syncing our data with the server and it handles the syncing based on the current connection status.

Redux Offline makes it simple for developers to handle offline capability. Check it out on Github.

PostgreSQL and Docker

To store the data from our app we will be using a PostgreSQL database. You can have your instance running anywhere but for this example, I will be creating a local instance using Docker.

With Docker installed, run the following command in a terminal to pull the PostgreSQL image from Docker.

docker pull postgres

That will take a minute or two to download the image, but once that is complete run the following command to start a local instance.

docker run --name local-postgres -e POSTGRES_PASSWORD=password -d -p 5432:5432 postgres

This should start a local instance immediately. To confirm you can run the following command to check the current Docker processes.

docker ps
Image for post
Image for post
Screenshot by the author

Next, we will create a new database from within the new container we just started. Run the following commands.

docker exec -it 16856b6f5f bash
root@16856b6f5f:/# psql -U postgres
postgres=# CREATE DATABASE imageuploads;
postgres=# \q
root@16856b6f5f:/# exit

Now we have our local instance ready and an empty database created within that instance. In the next section, we will use Sequelize to create migrations for setting up our database tables.

NodeJS API

We will be creating a simple NodeJS API with Express and Sequelize. Express is a framework used to create REST APIs and Sequelize is an ORM that we will use to communicate with our PostgreSQL database. In the API we will build endpoints for creating new entries for image uploads and also for fetching a list of all of the image uploads currently in the database.

To start we’ll create a new empty NodeJS project. Run the following commands in a terminal to set up an empty package.json and then add in our dependencies

mkdir offline-uploads-api
cd offline-uploads-api
npm init -y
npm install --save express sequelize pg pg-hstore cors body-parser
npm install --save-dev sequelize-cli

Next, we will initialize the Sequelize configuration by running the following command.

npx sequelize-cli init

This will create a few new folders in our project:

  • config/
  • models/
  • migrations/
  • seeders/

We will be creating new models and migrations here shortly, but first, we need to add our local PostgreSQL instance to the development configuration in config/config.json

Now Sequelize knows where to find our database so we can create a table where we will be storing all of our image uploads. We will use the following command to create a model and migration for the uploads table.

npx sequelize-cli model:generate --name uploads --attributes id:bigint,file_name:string,image:blob

This will generate a model that we will make a couple of updates to. Update the contents of models/uploads.js to the following code.

For our example, we will actually be storing the images as base64 strings in the database. This is a valid approach to storing images, there is also the option of using something like S3 to handle the file storage. If you’re interested in how to do that be sure to check out my article below.

Also, be sure to update the migration for adding the uploads table to the database to the following code.

To execute the migration and add the table to the local database instance run the following command which will run everything in the migrations folder that hasn’t yet been applied to the database.

npx sequelize-cli db:migrate

After this executes successfully we should be able to query our database and see that we have a single table now name uploads with five columns and currently has no rows of data.

Now that we have the database ready to go we are ready to add the API endpoints for creating and fetching uploads. We will create the following endpoints:

  • POST /uploads
  • GET /uploads

Let’s create those now. We will create a new file at routes/uploads.js and add the following code.

Very simple as you can see. We create two endpoints and either write or fetch records in the database. Just one more file to finish off our API. We create a new file called index.js.

And that’s all the code we need for API. We will be calling the API in the next section from our mobile app. You can run the API by running the following command.

node index.js

React Native and Redux Offline

With everything else in place now we can move on to building our user interface. The first step is to generate a new React Native application using the React Native CLI. Run the following command in a terminal to kick this off.

npx react-native init OfflineImageUpload
Image for post
Image for post
Screenshot by the author

This will take a few minutes to create the new app, but once that is complete we can move into the project directory and install the dependencies we’ll need for implementing Redux Offline.

cd OfflineImageUpload
npm install --save redux react-redux @redux-offline/redux-offline@native @react-native-community/async-storage @react-native-community/netinfo react-native-image-picker
npx pod-install

That last command will link everything for the native apps. You can run the app in the iOS simulator with the following command.

npx react-native run-ios

The directory structure we are using for the app will likely look familiar if you’ve ever worked on a Redux application before. We create the following directories in the root of the project:

  • actions/
  • components/
  • containers/
  • reducers/

We will start by creating the Redux actions for our app. So the actions are the functions we use to update the current state of the app’s data store. Create a file in actions/index.js and add the following code.

This is the spot where we set up Redux Offline. We decorate the actions with offline decorators to specify the endpoints to call for the actions and what data to pass. Different actions can trigger based on if calling the API calls or fails. If the network connection is determined to be offline it will wait for the connection to be restored before it sends all the uncommitted actions to the API.

Next, we will need to create a reducer that holds the list of uploads and consumes the results of the actions we created above. Create a new file at reducers/uploads.js and add the following contents.

Typically we’d create a root reducer to combine all of our reducers. We only have a single one, but we’ll still follow the convention as it leaves it open that we can easily add more later. Create a file at reducers/index.js and add this code.

Next, we need a component where we can display the list of images along with a way to upload additional images. Create a file at components/UploadList.js and fill it with these contents.

s

You can see in this component we are expecting uploads, fetchUploads, and addUploads to be passed in as props. We will do that in the next step when we create a container to map our actions and reducer to the props of this component.

We are now ready to create a container that will tie our actions and reducer together. Create a file named containers/UploadListContainer.js and add the following code.

Here we have mapped both state (reducers) and dispatch (actions) to props for the UploadList component we just created.

That’s it for the components we have to create for the app. Now we just tie the rest of it together in our main App.js file.

Here we are simply pulling in the container we just created and displaying it in the UI. We also configure our Redux store with the offline middleware provided by Redux Offline.

That takes care of the code to make our app work. Check out our handiwork below.

Image for post
Image for post
Screen capture by the author

Check out the example code on Github:

That wraps things up for this example. If you are interested in building offline-capable mobile apps I hope this has given you some insight into a simple path to do just that. Thanks for reading!

The Startup

Medium's largest active publication, followed by +734K people. Follow to join our community.

Matthew Brown

Written by

I am a software architect and lead developer. My passions include software development, anything technology related, and cars.

The Startup

Medium's largest active publication, followed by +734K people. Follow to join our community.

Matthew Brown

Written by

I am a software architect and lead developer. My passions include software development, anything technology related, and cars.

The Startup

Medium's largest active publication, followed by +734K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store