Build an anonymous chat app in React Native

Introduction

In this tutorial, we will be using React Native to build our Android application. React Native let's you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components. To learn more about React Native, please visit here.

Setting up React Native

First, we need to install the React Native CLI if we don't already have it. To install React Native, we run:

npm install -g react-native-cli

After installing the CLI, it's time to create our project. Open up a terminal, and create a new project called pubchatusing the following command:

react-native init pubchat

We wait until React Native does all its installations, then we can change directory into the new project and run the application using the following command:

1//change directory to pubchat
2cd pubchat
3//run the application
4react-native run-android

Please note, that before running the run-android command, you should have an emulator running, or an android device connected via adb.

You can read more on setting up React Native Android app from https://facebook.github.io/react-native/docs/android-setup.html

At this point, we should see this kind of screen:

anonymous-chat-react-native-welcome

However, you may run into an error like this:

anonymous-chat-react-native-error

To fix the error, all you need to do is to run this command:

react-native start

Setting up Pusher

At this point, React Native is ready and set up. We need to setup Pusher, as well as grab our app credentials.

We need to sign up on Pusher and create a new app, and also copy our secret, application key and application id.

anonymous-chat-react-native-create-app

We then need to install the required libraries:

npm install native-base pusher-js pusher express body-parser --save

In the above bash command, we installed 4 packages. I will explain what the four packages do below:

  • native-base: An essential cross-platform UI components for React Native. This helps us to reduce time writing and styling UI components ourselves.
  • pusher-js: This is the official Pusher JavaScript client. We'll be using its React Native library to subscribe and listen to events in our application.
  • pusher: This is the official Pusher library for Node.js. We will be using Node.js for our API, so this library will come in handy.
  • express: This is a Node.js web framework which we'll use to create our API.
  • body-parser: This library is used by Express to parse body requests.

After installing these packages, we need to link them with React Native, so we run the following command:

react-native link

**Please note that because we will be using Fetch to perform AJAX request, we would need to go to our android manifest located in android/app/src/AndroidManifest.xml and add the following permission: **

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Creating our API

First, let's create a new file called server.js, which serves as our API in our root folder and place in the following contents into it:

1// server.js
2
3//require express
4var express = require('express')
5//define app as in instance of express
6var app = express()
7//require bosy-parser
8var bodyParser = require('body-parser')
9//require pusher
10var Pusher = require('pusher')
11//use bodyparser as a middle ware
12app.use(bodyParser.json())
13//instantiate pusher
14const pusher = new Pusher({
15  appId: 'XXX_APP_ID',
16  key: 'XXX_APP_KEY',
17  secret: 'XXX_APP_SECRET',
18  cluster: 'XXX_APP_CLUSTER',
19  encrypted: true
20});
21//set cors middleware
22app.use(function(req, res, next) {
23  res.header("Access-Control-Allow-Origin", "*");
24  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
25  next();
26});
27//handle route postfunction
28app.post('/', function (req, res) {
29    pusher.trigger("pubchat", "message_sent", { message : req.body.message, name : "Anonymous" });
30    res.send({
31        message:'message_sent'
32    });
33})
34//listen on port and serve the app
35app.listen(3000, function () {
36  console.log('Example app listening on port 3000!')
37})

The code block above is our Express server setup. At the beginning of the file, we had required Express, Body-parser and Pusher libraries for Node.js respectively. We had also initialized a new Pusher object, passing in our appId, key, secret to it, and we set the output of the object to a constant called pusher.

Next, we set the CORS header to our request, using a middleware function.

Finally, we create a post handler for the \ route, and we then make a Pusher trigger to a channel called pubchat with an event called message_sent.

Let's take note of both the channel name and the event name used on this server. The channel name will be subscribed to, while we will listen for the event in our React Native app.

This is all we need at the server side for our API call to work.

Next, we go to our command line and run:

node server.js

Crafting up the application

Now let's replace our index.android.js with the following:

1// index.android.js
2
3/**
4 * Sample React Native App
5 * https://github.com/facebook/react-native
6 * @flow
7 */
8
9import React, { Component } from 'react';
10import {
11  AppRegistry,
12  StyleSheet,
13  Text,
14  View,
15  TextInput,
16  ScrollView
17} from 'react-native';
18// import native base components
19import { Container, Content, Footer, Button} from 'native-base';
20//import pusher
21import Pusher from 'pusher-js/react-native'
22//react-native class
23export default class pubchat extends Component {
24//load constructor
25constructor(props){
26  super(props);
27  //declare an array of messages
28  var messages_array = [];
29  // declare initial states
30   this.state ={
31    messages_array,
32    text:'' 
33   }
34
35  //instantiate pusher
36  var pusher = new Pusher('XXX_APP_KEY', {
37    cluster: 'XXX_APP_CLUSTER'
38  });
39  //subscribe to the public chat channel
40  var my_channel = pusher.subscribe('pubchat');
41  //bind and listen for chat events
42  my_channel.bind("message_sent", (data)=> {
43     this.state.messages_array.push(data);
44        this.setState({
45          text:''
46        })
47  });
48}
49
50  //function that sends messahe
51  send_message(){
52    //check that the text input isnt empty
53    if(this.state.text !=""){
54      fetch('XXX_IP_TO_MY_ROUTE', {
55        method: 'POST',
56        headers: {
57          'Accept': 'application/json',
58          'Content-Type': 'application/json',
59        },
60        body: JSON.stringify({
61          message: this.state.text
62        })
63      })
64      .then((response) => response.json()) 
65      .then((responseJson) => {}) 
66      .catch((error) => { console.error(error); });
67    }
68  }
69
70
71  //function that loops over our messages and displays them
72  loop(){
73      var element = [];
74     for (var index = 0; index < this.state.messages_array.length; index++) {
75
76            element.push(<View key={"container"+index} >
77                            <Text key = {"author"+index}>
78                              {this.state.messages_array[index].name}
79                            </Text>
80                            <Text key = {index} style={styles.bubble_you} >
81                              {this.state.messages_array[index].message}
82                            </Text>
83                        </View>);
84        }
85         return element;
86  };
87
88  //render function that actually shows the page
89  render() {
90    //execute the loop function and store its response into a variable
91    myloop = this.loop();
92
93    return (
94      <Container>
95      <ScrollView >
96        <View style={styles.container}>
97          <Text style={styles.welcome}>
98            Welcome to the public chat room!
99          </Text>
100              {myloop}
101        </View>
102        </ScrollView>
103        <Footer >
104          <TextInput
105            value ={this.state.text}
106            style={{width: '80%'}}
107            placeholder="Enter Your message!"
108            onChangeText={(text) => this.setState({text})}
109          />
110          <Button onPress={()=> this.send_message()}>
111            <Text>Send</Text>
112          </Button> 
113        </Footer>
114      </Container>
115    );
116  }
117}
118
119//stylesheet 
120const styles = StyleSheet.create({
121  container: {
122    flex: 1,
123  },
124  welcome: {
125    fontSize: 20,
126    textAlign: 'center',
127    margin: 10,
128  },
129  bubble_you: {
130    color: '#fff',
131    backgroundColor: '#00b0ff',
132  width: '50%',
133  borderRadius: 25,
134  padding: 7,
135  marginBottom: 2,
136  },
137});
138
139AppRegistry.registerComponent('pubchat', () => pubchat);

Above, we have imported the Native Base component to help us with our UI styling instead of the default React Native components. Next, we imported Pusher for React Native, then we declare our React Native class.

We proceed by creating a constructor, and in our constructor, two states are declared namely: messages_array and text, which respectively represent our array of messages as well as the current text that is being typed.

Next, we instantiate Pusher, passing in our APP_KEY. Then we subscribe to the channel which we are emitting to from the server called pubchat and also we listen to the message_sent event which we also trigger from our server.

While listening to the message_sent event, we push the data that arrives at our messages_array state, and also set our text state to empty.

Next, we create a function which sends our messages to the server, so it can be sent to Pusher. In this function, we first check if the state is empty, to avoid sending empty messages to the server.

Next, we use the fetch API provided by React Native to send an AJAX request to our server which we created earlier.

Note: If you use IP address such as 127.0.0.1 or localhost, the request is most likely going to fail. This is because, in React Native, localhost or 127.0.0.1 refers to the internal application. Please use the network IP for your system instead.

Next, we define a loop function, which loops through all our messages and pushes them into an array which is being returned. This function would be used to display all messages on the UI.

The next function is our render function, which is a native React Native function. First, we declare a variable called myloop and set it to our loop function.

In our return statement, the myloop variable was rendered, so it can display its content. Also, take a look at the footer tag we have there. In the footer tag, we have a text input and a button.

The text input text is used to set the text state anytime the text changes using the onChangeText event of the button. Notice that our button also calls the send_message function anytime it is pressed by binding it to its onPress function.

Finally, we defined some style sheets.

At this point if we reload our application, our app would look like the following:

anonymous-chat-react-native-design

At this point, once our server is up and running, we should go to the application, type in a message, then send.

Here is a demo of what we have built:

anonymous-chat-react-native-demo

Conclusion

In this article, we have demonstrated how to make a public anonymous chat application in Android using React Native. We have secured the design choices which are important to begin with, and the cases above ought to help you fill in the holes and give an outline of a portion of the other design choices accessible to you.