In the last three parts of our Tutorial series, we managed to create our API which is going to accomondate all the business logic for our Todo application.

Today, we are going to continue with the front-end implementation in React Native.

As the name implies, React Native is a web framework based on React, which is specifically designed to create native mobile applications.

So someone may ask why shouldn't we use let's say Cordova instead or any other web framework?

Well, React has the word Native in its name and what that means is that the app is truly Native.

The Javascript that we provide, compiles down to either Objective C for iOS development or to Java for Android development.

You can find more info about React Native in the official website, as well as the installation instructions for your operating system.

A list of Todo items - React Native

Create a new React native project

Run react-native init ReactNativeTodoList to initialize a new react-native project.

Let's move inside the project folder cd ReactNativeTodoList and open it with our editor.

In the root directory there are two files:

  1. index.android.js
  2. index.ios.js

For our application we are going to focus on the index.ios.js as in this tutorial we will create an iOS application.

We will start by running our app so as to see all the foregoing changes.

In a seperate terminal window and inside our project folder execute:

react-native run-ios

React native iOS start screen

What this command does, is to compile our project's files and run our application inside the emulator.

Furthermore, whaterever change we make to our index.ios.js file is immediately reflected in the emulator.

Cool isn't it?

Let's start by deleting the generating code and creating our own implementation step by step.

In index.ios.js

 // Importing all the dependancies
 import React, { Component } from 'react';
 import {
   AppRegistry,
   View,
   Text,
   StyleSheet
 } from 'react-native';

 // Creating a new component
 var ReactNativeTodoList = React.createClass({
   render: function() {
     return (
       <View style={styles.container}>
         <Text>
           Welcome to Phoenix API with React Native Tutorial!
         </Text>
       </View>
     )
   }
 });

 // Defining some basic styles
 var styles = {
   container: {
     flex: 1,
     alignItems: 'center',
     justifyContent: 'center'
   }
 };

 AppRegistry.registerComponent('ReactNativeTodoList', () => ReactNativeTodoList);

The next step is to install react-native-swipe-list-view

This component will achieve a user friendly UI by allowing us to swipe on each todo item and access actions like edit or delete

npm install --save react-native-swipe-list-view

After the installation is completed we should enable the ListView component in our application and implement the SwipeListView

import React, { Component } from 'react';
import {
  AppRegistry,
  View,
  Text,
  ListView, //New
  StyleSheet,
} from 'react-native';

// New
import { SwipeListView } from 'react-native-swipe-list-view';

var ReactNativeTodoList = React.createClass({
  // New
  getInitialState: function() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    var todoItems = [
      {id: 1, title: 'Todo 1'},
      {id: 2, title: 'Todo 2'},
      {id: 3, title: 'Todo 3'}
    ];
    return {
      dataSource: ds.cloneWithRows(todoItems)
    };
  },
  
  render: function() {
    return (
      <View style={styles.container}>
        <View style={styles.header}>
          <Text>
            Welcome to Phoenix API with React Native Tutorial!
          </Text>
        </View>
        <View style={styles.content}>
          <SwipeListView
            dataSource={this.state.dataSource}
            renderRow={(todo) =><View style={styles.todo}><Text>{todo.title}</Text></View>}
            renderHiddenRow={data => (
              <View style={styles.rowBack}>
                <Text>Delete</Text>
              </View>
             )}

            rightOpenValue={-45}/>
        </View>
      </View> 
    )
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'stretch'
  },
  header: {
    flex: 1,
    alignItems: 'center',
    marginTop: 60,
    borderBottomWidth: 0.5
  },
  content: {
    flex: 7,
  },
  todo: {
    borderBottomWidth: 0.5,
    height: 60
  },
  rowBack: {
    alignItems: 'flex-end',
    justifyContent: 'center'
  }
});

AppRegistry.registerComponent('ReactNativeTodoList', () => ReactNativeTodoList);
React native iOS start screen
React native iOS start screen

Let's see what we implemented above.

In React as well as React Native the rendering engine works with the state of each individual component.

Each component when it is mounted it can have an initial state (and it should have, based on the documentation).

In getInitialState method we define some hard coded todo items so as to help us visualize our application.

With the help of react-native-swipe-list-view that we previously installed, we can now swipe each individual todo item and reveal a new action like the Delete button.

Last but not least, we added more styles to our StyleSheet and positioned accordingly, our view components with the help of Flexbox.

The moment of truth

As the header implies, it is time to see our Phoenix API in action.

Let's refactor our code and get rid of those hard coded todos.

import React, { Component } from 'react';
import {
  AppRegistry,
  View,
  Text,
  ListView,
  StyleSheet
} from 'react-native';

import { SwipeListView } from 'react-native-swipe-list-view';

var ReactNativeTodoList = React.createClass({
  getInitialState: function() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    return {
      dataSource: ds.cloneWithRows([])
    }
  },

  // New
  componentDidMount: function() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    return this._getTodosFromApi()
      .then((data) => {
        this.setState({
          dataSource: ds.cloneWithRows(data)
        });
      });
  },

  // New
  _getTodosFromApi: function() {
    return fetch("http://localhost:4000/api/todos")
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson;
      });
  }, 

  render: function() {
    var _this = this;
    return (
      <View style={styles.container}>
        <View style={styles.header}>
          <Text>
            Welcome to Phoenix API with React Native Tutorial!
          </Text>
        </View>
        <View style={styles.content}>
          <SwipeListView
            dataSource={_this.state.dataSource}
            renderRow={(todo) =><View style={styles.todo}><Text>{todo.title}</Text></View>}
            renderHiddenRow={data => (
              <View style={styles.rowBack}>
                <Text>Delete</Text>
              </View>
             )}

            rightOpenValue={-45}/>
        </View>
      </View> 
    )
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'stretch'
  },
  header: {
    flex: 1,
    alignItems: 'center',
    marginTop: 60,
    borderBottomWidth: 0.5
  },
  content: {
    flex: 7,
  },
  todo: {
    borderBottomWidth: 0.5,
    height: 60
  },
  rowBack: {
    alignItems: 'flex-end',
    justifyContent: 'center'
  }
});

AppRegistry.registerComponent('ReactNativeTodoList', () => ReactNativeTodoList);

As you can see the changes that we did are minimal.

We replaced the hard coded array of todo items with the call of _getTodosFromApi method.

An interesting fact about the asynchronous request that we perform is that it is using the ES6 concept of promises.

More info can be found in the official documentation.

If we refresh the emulator with CMD + R we will see that there aren't any todo items yet.

In another terminal window and inside the todo_list_phoenix_api start the server.

mix phoenix.server

    [info] Running TodoListPhoenixApi.Endpoint with Cowboy using http://localhost:4000
    

Cowboy?

Yeah, thats the name of Phoenix server

In another terminal window open the phoenix console.

iex -S mix

import TodoListPhoenixApi.Factory

insert_list(6, :todo)

[%TodoListPhoenixApi.Todo{__meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
  id: 5, inserted_at: #Ecto.DateTime<2016-10-13 21:07:53>,
  title: "I have something to do",
  updated_at: #Ecto.DateTime<2016-10-13 21:07:53>},
 %TodoListPhoenixApi.Todo{__meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
  id: 6, inserted_at: #Ecto.DateTime<2016-10-13 21:07:53>,
  title: "I have something to do",
  updated_at: #Ecto.DateTime<2016-10-13 21:07:53>},
 %TodoListPhoenixApi.Todo{__meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
  id: 7, inserted_at: #Ecto.DateTime<2016-10-13 21:07:53>,
  title: "I have something to do",
  updated_at: #Ecto.DateTime<2016-10-13 21:07:53>},
 %TodoListPhoenixApi.Todo{__meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
  id: 8, inserted_at: #Ecto.DateTime<2016-10-13 21:07:53>,
  title: "I have something to do",
  updated_at: #Ecto.DateTime<2016-10-13 21:07:53>},
 %TodoListPhoenixApi.Todo{__meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
  id: 9, inserted_at: #Ecto.DateTime<2016-10-13 21:07:53>,
  title: "I have something to do",
  updated_at: #Ecto.DateTime<2016-10-13 21:07:53>},
 %TodoListPhoenixApi.Todo{__meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
  id: 10, inserted_at: #Ecto.DateTime<2016-10-13 21:07:53>,
  title: "I have something to do",
  updated_at: #Ecto.DateTime<2016-10-13 21:07:53>}]

Nice.

Now we that we got enough todos, let's refresh our emulator CMD + R.

ios-todo-items-index-1

Voila!

I think the only thing missing is styling our todo items.

Spice up

import React, { Component } from 'react';
import {
  AppRegistry,
  View,
  Text,
  ListView,
  StyleSheet
} from 'react-native';

import { SwipeListView } from 'react-native-swipe-list-view';

var ReactNativeTodoList = React.createClass({
  getInitialState: function() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    return {
      todos: [],
      dataSource: ds.cloneWithRows([])
    }
  },

  componentDidMount: function() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    return this._getTodosFromApi()
      .then((data) => {
        this.setState({
          dataSource: ds.cloneWithRows(data)
        });
      });
  },

  _getTodosFromApi: function() {
    return fetch("http://localhost:4000/api/todos")
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson;
      });
  }, 

  render: function() {
    var _this = this;
    return (
      <View style={styles.container}>
        <View style={styles.header}>
          <Text style={styles.headerText}>
            Welcome to Phoenix API with React Native Tutorial!
          </Text>
        </View>
        <View style={styles.content}>
          <SwipeListView
            dataSource={_this.state.dataSource}
            renderRow={(todo) =><View style={styles.todo}><Text style={styles.todoText}>{todo.title}</Text></View>} // New
            renderHiddenRow={data => (
              <View style={styles.rowBack}>
                <Text style={styles.deleteBtn}>Delete</Text> // New
              </View>
             )}
            rightOpenValue={-45}
            disableRightSwipe={true}/> // New
        </View>
      </View> 
    )
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'stretch',
    backgroundColor: '#ecf0f1' // New
  },
  header: {
    flex: 1,
    justifyContent: 'center',  // New
    alignItems: 'center',
    borderBottomWidth: 0.5, 
    backgroundColor: '#1abc9c' // New
  },
  headerText: {
    fontWeight: '600' // New
  },
  content: {
    flex: 7,
  },
  todo: {
    borderBottomWidth: 0.5,
    height: 60,
    backgroundColor: '#34495e', // New
  }, 
  // New
  todoText: {
    fontWeight: '600',
    fontSize: 16,
    color: '#fff',
    marginTop: 21,
    marginLeft: 100
  }, 
  rowBack: {
    flex: 1, // New
    alignItems: 'flex-end',
    justifyContent: 'center',
    backgroundColor: '#e74c3c' // New
  },
  deleteBtn: {
    alignItems: 'center',
    justifyContent: 'center'
  }
});

AppRegistry.registerComponent('ReactNativeTodoList', () => ReactNativeTodoList);
Todo items index page styled
Todo items index page styled showing DELETE button

Phew.. :)

I created a new repository with all the React Native code so far.

Till next time.