app development · react native

Build an Expense Manager App with React Native – Part 8

Wraping up

Working with react native has a learning curve and I spent a lot of time trying to understand how to debug configuration errors. But once the core of the app is setup (mainly the redux store and its persistence), the UI development is pretty straight forward, and that’s a real advantage compared to native android java development.

Things to fix

I would have loved to take more time to spare for unit testing, saga testing etc but I have encountered a lot of errors with jest (basically, I have many React Native modules to mock etc). Once you wrap your head around how (and what modules) to mock, unit testing with jest is not hard.

Final result

https://zeitouna.eu/index.php/s/dxqEqbpABopzSMA/download

app development · react native

Build an Expense Manager App with React Native – Part 7

Testing Redux with Jest


Install redux-mock-store, this library will help us mock redux’s state in our tests.

We are not going to use react-test-renderer, instead add Enzyme:

We will use jest to implement tests for our redux state. In __tests__ folder, add spec files for each component we want to test. For example, test that the SplashScreen is rendered without crashing:

app development · react native

Build an Expense Manager App with React Native – Part 6

Add connection status Banner

We are going to use the AppStateRootSaga we setup previously to show a banner to the user to indicate when he is online or not.

Use the following Banner Component (credits: https://pusher.com/tutorials/offline-react-native-part-2):

First map the offline.isConnected property in the MainScreen :

Now on each state update, check the connectivity value:

Include the NetworkStatusBanner:

app development · react native

Build an Expense Manager App with React Native – Part 5

Make the app work offline

In order to make the app usable when no Internet connection is available we will use some neat libraries. One of these are redux-persist, which enables to save the state store of the app to local storage. Then we will need redux-offline-queue which makes it possible for the user to add new expenses when offline. When a connection is detected, all changes will be synced transparently.

Add the dependencies:

Import Redux Offline Queue reducer, in reducers/index.js:

Important

Notice above that we named our DataReducer as data, so in order to access it you will need to change calls to the state to this.props.data.expenses
for example

Now, we need to integrate the ROQ middleware with our Redux Saga configuration in our App.js:

It’s important to make sure the consumeActionMiddleware is the last to being called.

Configure Network Connectivity Saga

We will need to separate the Data Saga from the connectivity specific saga. So rename sagas/index.js to sagas/DataSaga.js. Create a new sagas/index.js:

Create sagas/AppStateRootSaga.js

The saga above will listen for all connectivity changes and save the connectivity status in the store.

At this point our state store looks like this :

Configure data persistence with redux-persist

Redux Persist enables to persist the state store to localStorage when the app is killed.

Add the dependency:

Edit App.js to add the persistence config:

app development · react native

Build an Expense Manager App with React Native – Part 4

Adding localization

I’ve tried multiple localization libraries for React Native but finally settled for i18next.

Add the dependency:

To keep things simple, all translations will be in the same folder. In a bigger app, it is recommended to keep component specific translations alongside the component, that way it is easier to clean up if you decide to delete a component, reuse it in another project, etc.

Create a file i18n/index.js:

Create a json file for each supported language. e.g.:

finally call the i18n initialization in your App.js:

To translate a string in a component, import i18n then call i18next.t:

Get current system locale and wiring it

We will use RN NativeModules to get the current device locale, in i18n/index.js, add: (source https://jonrh.is/react-native-locale-strings/)


app development · react native

Build an Expense Manager App with React Native – Part 3

connect our containers to the Redux Store

We will user Redux’s connect() in our containers (screens) in order to make them connected to the Redux Store.

Example: show a loader when expenses are being fetched from the server, show an empty view if there none and show an error view if the API call fails. Edit MainScreen:

Now you can use conditional rendering to display certain components when a condition is meet. For example we show an Loader when the app is fetching expenses from the API. (this.state.isLoading = true)

app development · react native

Build an Expense Manager App with React Native – Part 2

Using Redux and Redux SAGAS

Redux is a very important library to manage the app’s state. This state will be accessible across all our app’s components without the need to pass the state to each component through props

In our App.js we create the app’s store, to which we pass the initial state

The rootReducer initiates the state:

You should see the reducer as the way to edit the state through sending signals to the store. These signals are referred to as actions. Each action must have a type.

We will declare the following actions:

The state is immutable. You updated it by copying the current state’s data and adding in new data.

Create a new file in App/actions. let’s name it index.js. This will hold all the dispatch action code for our 4 actions above.

Comes Redux SAGA

Redux Saga is a middleware that comes in handy to manage the asynchronous API calls. Setup the middleware in App.js like this:

Manage REST API calls

Axios is a useful library to make HTTP calls. We will use it alongside redux-saga to make our network requests.

Add the dependency:

Then import it in our saga file:

Set API token

Usually you will access secure APIs through some authorization mechanism. In our example API, we will need an auth token. It is recommended to put it in an env variable and access it using react-native-dotenv. First, update your babel presets to add this package:

Create a file called .env at the root of the project:

You can access this value anywhere in your code by importing it:

Add authorization header to API calls

Using generator functions

Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

A generator is a function that can stop midway and then continue from where it stopped.

https://codeburst.io/understanding-generators-in-es6-javascript-with-examples-6728834016d5

If you are using React Navigation version 3+, it is recommended you don’t try to make redux manage the nav state and leave to RNN to do that.

Can I store the navigation state in Redux too?
This is technically possible, but we don’t recommend it – it’s too easy to shoot yourself in the foot and slow down / break your app. We encourage you to leave it up to React Navigation to manage the navigation state. But if you really want to do this, you can use react-navigation-redux-helpers, but this isn’t an officially supported workflow

— from RNN docs

Testing Sagas

For a start, head to this excellent article about testing redux sagas https://medium.com/@karanjariwala/testing-redux-sagas-with-a-plan-e59124c5d139


Head to part 3, to see how to connect our containers to the Redux store.

app development · react native

Build an Expense Manager App with React Native – Part 1

Design and UI

Through this succint tutorial, we will go through the design and development of a small expense manager in React Native. This app will help a user save and tag expenses on a remote server using a REST API. It will need to store data locally when no Internet connection is available and sync it when a connection is detected.

The following is not intended to be a full tutorial, but rather a compilation of notes and troubleshooting tips.

UI Design

The app will have 3 main screens:

  • SplashScreen
  • MainScreen containing a list of all the expenses
  • AddExpenseScreen where the user can input the amount, category of the expense

Setup Environment

For this app, I am going to use React Native without Expo. You can see why not to use Expo here

Set ANDROID_HOME variable

You will need to make the ANDROID_HOME variable available. You can do this by setting it in the end of ~/.bashrc or ~/.bash_profile files:

Install dependencies using yarn

If you don’t have yarn installed, head to this page. I do not recommend using npm commands alongside yarn’s.

Now use the cli to scaffold the app skeleton:

This will generate the following structure:

Run the app

Now test everything by running:

You will need to run an emulator or plugin a device beforehand.


App Architecture

# Requirement 1: Persistence

https://github.com/rt2zz/redux-persist

# Requirement 2: Offline usage

https://github.com/redux-offline/redux-offline


Create the UI

First, we are going to create the three screens with dummy data.

Create a folder called components at the root of the app’s folder and under components/screens create three files: SplashScreen.js, MainScreen.js and AddExpenseScreen

In order to navigate between all different screens, I am going to use React Native Navigation.

Install it:

Along with React Native Navigation, we will be using React Native Redux which will offer us the possibility to manage an app wide store state of the app. Install React Native Redux:

redux-logger is quite handy to see the content of the store state

Setup Navigation

Create a new file name Navigator.js in a Navigation folder:

Setup the reducer

Add a new file caller Reducer.js at the app’s root folder:

We will handle persisting the store state later. Now, import the newly created reducer in the App.js file:

Create SplashScreen

The Splash Screen contains a simple text and no significant code. Here is the result:

Now add the logic to transition from SplashScreen to the MainScreen. We will just use a timeout. Add this to the SplashScreen component:

Create MainScreen

The main screen is comprised of 3 components:

  • Header
  • AddExpense Button
  • List of all Expenses

We are going to use the components from React Native Elements. Add the dependencies:

Checkout this excellent tutorial on how to use flex for designing the UI: https://medium.com/wix-engineering/the-full-react-native-layout-cheat-sheet-a4147802405c

Create AddExpenseScreen

This screen has the following sections:

  • A header with two buttons
  • An input text for the expense amount
  • A list to select the expense category
  • A date picker
  • A text input for expense comment

Add the following dependencies:

react-native-sectioned-multi-select is a handy select component which we will use to let the user see a predefined categories list. This list is the following:

The selector is very easy to use:

Similarly the date picker is used like this:

The screen will look like this. It is a little bit different from the first design but I have found this card based layout a little bit easier to use.

Now that the visual components are done, head to part 2 of this tutorial to see how we fetch data from the API.