Build Once, Run Everywhere: How We Created a Universal Template Project in React Native

React Native is a JavaScript framework that allows you to build cross-platform mobile apps that feel truly native and run smoothly on iOS and Android. React Native arose from React, which offers a pattern for building web and mobile user interfaces in a declarative, efficient, and flexible way.

Both open-source libraries are maintained by Facebook and a community of independent developers. React and React Native have been successfully adopted and are actively being used by a number of corporations such as Airbnb, Buffer, Bleacher Report, Feedly, HelloSign, Imgur, and Netflix.

Unlike other cross-platform development solutions (Cordova, Ionic, or Titanium, for instance) that use webviews for the graphical interface, React Native uses its native rendering APIs in Objective-C (iOS) or Java (Android), so your app renders using native views. This lets developers build nice cross-platform experiences without losing any quality in UI performance.

React Native makes Java’s “write once, run anywhere” slogan reality, except for the fact that JavaScript is used.

We’ve already been experimenting with React Native for a while; these experiments have resulted in a number of interesting components. This time, however, we’ve decided to create something really special and useful...

The Template Project

To optimize our development processes and provide our clients with high-quality products, we wanted to create a robust template app that would work properly on both platforms – Android and iOS – and that could be used as a base for our future projects. As you might have guessed, we used React Native to bring it to life.

Our React Native Template Project contains user flows that are common to almost all apps. These flows include:

  • Login

  • Item list

  • Item details

  • Logout

The project is now available on GitHub, so you can easily check it out.

This article isn’t a detailed tutorial on how to implement these flows in React Native. On the contrary, this article reveals the basics of React Native – we’ll take a look at some regular patterns as well as helpful JavaScript libraries that can be used to create a React Native app.

React Native knowledge base

  • Components

If you aren’t familiar with React Native, you’ll need some basic knowledge to get through the rest of the article. This section will also give you some understanding of common development patterns and reasons why we used them in our development process. Let’s start with the basics – React Components, a concept also used in React Native.

A React.Component object is the smallest atomic unit of a graphical interface. A component takes parameters (called “props”) and returns the view hierarchy to the display via the render method.

export default class RepositoryListItem extends React.PureComponent {

 _onPress = () => {
   const {navigate} = this.props.navigation;
   navigate(consts.REPOSITORY_DETAILS_SCREEN, {repository: this.props.repository})
 };

 render() {
   return (
     <TouchableHighlight onPress={this._onPress}>
       <View style={itemStyles.itemStyle} {...this.props}>
         <Text style={itemStyles.itemTitleStyle}>{this.props.title}</Text>
         <Text style={itemStyles.itemDescriptionStyle}>{this.props.description}</Text>
       </View>
     </TouchableHighlight>
   )
 }

}
}

Each item in the hierarchy is a component that, in turn, takes its own props and contains other components.

item hierarchy react native

With this approach, we can create an app based on modular and reusable blocks.

  • The component lifecycle

Each component has a lifecycle and state. Whenever these change, the Render method is invoked.

the component life cycle react native

We have to control the modification of our data to prevent unnecessary rendering of the user interface. To deal with this, we chose the Redux architecture.

  • The Redux library

In the previous section, we explained that when you create an app using React, it’s essential to keep data in a consistent state. Redux helps us do that.

The UI state is complex – we need to manage active routes, selected tabs, spinners, pagination controls, and so on.

Managing this ever-changing state is hard. If a model can update another model, and then a view can update that model, which updates another model that, in turn, may result in another view being updated... At some point, you no longer understand what happens in your app as you lose control over when, why, and how its state changes. When a system is opaque and non-deterministic, it’s hard to reproduce bugs or add new features.

Redux offers a solution to create a single global state for the whole app; this solution is called store. Each component can initiate an action to change the store. Each component that observes the state of the store receives new data in the form of props when the store is changed.

how the redux library works react native

  • The Saga library

Many mobile applications have to send requests to the backend or a database. These operations usually take a long time and bring side-effect data to our store; for such cases, the Redux architecture provides synchronous and consistent performance.

To optimize this process, we can use middleware, an intermediary between two events: the sending of an action and the receiving of that action by the reducer. In fact, middleware is actually a function that takes store as an argument, returns a function that takes the next function as an argument, and then returns another function that takes an action as an argument.

const someMiddleware = store => next => action => {
 ...
}

The next function plays a crucial role. We call this function when our middleware is done with the task we’ve assigned it. This function sends our actions to our reducer or other middleware. As such, we can perform asynchronous operations inside middleware.

We chose the Redux-Saga library, which allows us to create such middleware and provides useful functions to easily deal with Redux.

how the redux-saga library works react native

Redux-Saga provides the middleware function, which is implemented as a Generator function (this is a JavaScript concept that aims to make asynchronous tasks synchronous and consistent).

As we can see, using the yield keyword (used in Generators) and some functions from the Redux-Saga library, such as take, put, and call, we can intercept actions from components, send requests to the backend, and make the process of dispatching actions synchronous and consistent.

export function* loginFlow() {
 while (true) {
   const {username, password} = yield take(actions.LOGIN_ACTION);
   yield put({type: actions.PROGRESS, progress: true});
   yield call(authorize, username, password);
   yield put({type: actions.PROGRESS, progress: false});
 }
}
  • The Immutable library

Redux uses reducer functions to perform changes within the store, so when a reducer function catches some action, it creates a new object that holds a new state. Why does this object have to be new? Because this way we can compare references to two different objects (a new object and an old object); moreover, it’s much simpler than comparing object contents.

comparing references to two different objects the immutable library react native

At the very least, we have to create a new object using the Spread operator:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return { ...state, visibilityFilter: action.filter }
    default:
      return state
  }
}

We decided to use the immutable.js library because it guarantees immutability and does much of the heavy lifting behind the scenes to optimize performance and memory consumption. Our reducer function looks like this:

export default function loginReducer(state, action = {}) {
   switch (action.type) {
       case actions.ACTION_LIST_ERROR:
           return state.withMutations(state => state.set('loginError', action.error));
       case actions.ACTION_LIST_SUCCESS:
           const data = action.page === 1 ? action.list : state.get('data').concat(action.list);
           return state.withMutations(state => state.set('data', data));
       default:
           return state
   }
}
  • Platform-specific UI elements

As far as native UI development goes, we need to stick closely to a native platform’s rules. In other words, there should be no difference between a UI created in a native language and a UI created using React Native.

React Native has many components you can use to create great user interfaces. These components, in turn, support many style parameters that allow us to customize the look and behavior of the UI. However, these components are usually not enough to create a robust UI.

To fix this lack of native elements, we used the Native-Base library, which contains many platform-specific components.

platform-specific UI elements for Android the native-base library

[Platform-specific UI elements for Android] 

The Native-Base library allows us to use one component for both platforms without any extra adjustments.

We can also use the power of the Flexbox container, which is an effective and flexible tool for placing UI elements. This is applicable to both React Native’s standard UI elements and elements provided by Native-Base.

What else can our template do?

We wanted to create a project that covered the majority of our use cases. However, there are also system flows that usually happen behind the scenes.

We also gathered common tasks we always face, such as string localizations, maps, animations, permissions, icons, networking, data persistence, and so on, and put them into our Template Project as well.

We studied loads of open source JavaScript libraries to find the most effective solution for this project, all of which we’ve mentioned in this article.

Our goal was to create a template that supports our most commonly used flows. We ended up with a robust app for both Android and iOS.

React Native has proved once more to be an excellent tool for creating great mobile apps in a short timeframe.

Our project is free to access on GitHub, and we hope it will be useful for you. Of course, we’re going to improve and update our template, and we’re always glad to hear any suggestions or comments from other developers.  

4.0/ 5.0
Article rating
28
Reviews
Remember those Facebook reactions? Well, we aren't Facebook but we love reactions too. They can give us valuable insights on how to improve what we're doing. Whould you tell us how you feel about this article?