How I Built a Chat App in React Native

Krish Lulla
6 min readApr 6, 2020

React Native is an API that allows people to build apps on IOS and Android. An easy starter project is a chat app. Here is a simple tutorial.

Creating Our Server Using Firebase

We are going to use Firebase to create our server. This is a free program made by Google. Create a new app on Firebase on console.firebase.google.com.

I would recommend enabling Google Analytics on your app

Next, go to the authentication tab and click on the sign-in method option.

Here you can enable all the sign-in options that you would like. I would recommend using email/password and Google.

Writing Code

To start open a code editor. I would recommend using VS Code as it is very easy to use with React and React Native. Next, set up a folder with this directory structure.

Now we will connect the Chat and Main screens using react-navigation. We’ll put the following code in the App.js

import Main from './components/Main';
import Chat from './components/Chat';
// Import React Navigation
import { createStackNavigator } from 'react-navigation'// Create the navigator
const navigator = createStackNavigator({
Main: { screen: Main },
Chat: { screen: Chat },

export default navigator

This code will display a header bar. Now we can add a text box for the user.

That'll be in the Main.js

import {
StyleSheet,
TextInput, // 1. <- Add this
View,
} from 'react-native';...class Main extends React.Component {
state = { name: '' } // 2. <- Add the component state render() {
return (
<View>
<TextInput
style={styles.nameInput}
placeHolder="John Cena"
value={this.state.name}
/>
</View>
);
}
}...const offset = 24;
const styles = StyleSheet.create({
nameInput: { // 3. <- Add a style for the input
height: offset * 2,
margin: offset,
paddingHorizontal: offset,
borderColor: '#111111',
borderWidth: 1,
},
});

Now we’ll update the state when the user types.

// Inside the Main component...
onChangeText = name => this.setState({ name }); // 1.
// Inside the render() method...<TextInput
onChangeText={this.onChangeText} // <- Add this
style={styles.nameInput}
placeHolder="John Cena"
value={this.state.name}
/>

Next, we’ll add a title and a button to the screen

import {
StyleSheet,
Text, // <- Add Text
TextInput,
TouchableOpacity, // <- Add TouchableOpacity
View,
} from 'react-native';...render() {
return (
<View> // 2. Add the title
<Text style={styles.title}>Enter your name:</Text>
<TextInput
style={styles.nameInput}
placeHolder="John Cena"
onChangeText={this.onChangeText}
value={this.state.name}
/>
// 3. Add a button <TouchableOpacity onPress={this.onPress}>
<Text style={styles.buttonText}>Next</Text>
</TouchableOpacity>
</View>
);
}...const styles = StyleSheet.create({
title: { // 4.
marginTop: offset,
marginLeft: offset,
fontSize: offset,
},
buttonText: { // 5.
marginLeft: offset,
fontSize: offset,
},
})

Now we’ll add the press method.

onPress = () => {
// 1.
this.props.navigation.navigate('Chat', { name: this.state.name });
}

You should see something like this

Chat Screen

Now we’re going to start adding to the Chat.js

import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';class Chat extends Component {
render() {
return <View />;
}
}const styles = StyleSheet.create({});export default Chat;

This is some starter code to add to your Chat.js

import React from 'react';// 1.
import { GiftedChat } from 'react-native-gifted-chat';class Chat extends React.Component { // 2.
static navigationOptions = ({ navigation }) => ({
title: (navigation.state.params || {}).name || 'Chat!',
}); // 3.
state = {
messages: [],
};

render() { // 4.
return (
<GiftedChat
messages={this.state.messages}
/>
);
}
}export default Chat;
  1. Import GiftedChat. This is the component that we will use to do most of the heavy lifting.
  2. navigationOptions is used to configure how the navigation components (Header) look and act. We are passing an object with a title property that will set the title to either the state.params.name or a default value of Chat
  3. Create the component state and give it a messages prop then assign it to an array.
  4. Replace the View with the GiftedChat component and give it our state.messages to render.

Database

This is where we are going to add the backend that we made in Firebase. Create a Fire.js file. This is some basic code to add to the file.

class Fire {}Fire.shared = new Fire();
export default Fire;

Here we are importing and initializing Firebase. We initialize it with the API keys we get from the app

// 1.
import firebase from 'firebase'; class Fire {
constructor() {
this.init();
} // 2.
init = () =>
firebase.initializeApp({
apiKey: 'AIzaSyDLgW8QG1qO8O5WZLC1U8WaqCr5-CvEVmo',
authDomain: 'chatter-b85d7.firebaseapp.com',
databaseURL: 'https://chatter-b85d7.firebaseio.com',
projectId: 'chatter-b85d7',
storageBucket: '',
messagingSenderId: '861166145757',
});
}

To read and write from the database we need to be logged in.

After initializing, call our observeAuth function

  1. We want to get our auth, if we were signed in before then this will return a user if we weren’t then this will be null.
  2. onAuthStateChanged gets called as soon as firebase finds our auth.
class Fire {
constructor() {
this.init(); // 1.
this.observeAuth();
} // 2.
observeAuth = () =>
firebase.auth().onAuthStateChanged(this.onAuthStateChanged); // 3.
onAuthStateChanged = user => {
if (!user) {
try {
// 4.
firebase.auth().signInAnonymously();
} catch ({ message }) {
alert(message);
}
}
};
}

The following code will essentially allow us to receive messages.

// 1.
get ref() {
return firebase.database().ref('messages');
}// 2.
on = callback =>
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));// 3.
parse = snapshot => {
}// 4.
off() {
this.ref.off();
}

Now we will format the data correctly by changing the shape.

parse = snapshot => {  // 1.
const { timestamp: numberStamp, text, user } = snapshot.val();
const { key: _id } = snapshot; // 2.
const timestamp = new Date(numberStamp); // 3.
const message = {
_id,
timestamp,
text,
user,
};
return message;
};

Now we will create a way to send messages. We will create a timestamp for sent messages, make a send function, and save the message object with a unique ID

// 1.
get uid() {
return (firebase.auth().currentUser || {}).uid;
}// 2.
get timestamp() {
return firebase.database.ServerValue.TIMESTAMP;
}

// 3.
send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i]; // 4.
const message = {
text,
user,
timestamp: this.timestamp,
};
this.append(message);
}
};// 5.
append = message => this.ref.push(message);

Finally, we will add this code to our Components.js.

  1. We set up a callback that receives messages and then adds them to our current messages.
  2. When a component is not on the screen anymore we want to unsubscribe from the database
  3. We need to give GiftedChat a reference to our user
// 1.
componentDidMount() {
Fire.shared.on(message =>
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
}))
);
}// 2.
componentWillUnmount() {
Fire.shared.off();
}
get user() { // Return our name and our UID for GiftedChat to parse
return {
name: this.props.navigation.state.params.name,
_id: Fire.shared.uid,
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={Fire.shared.send}
user={this.user}
/>
);
}

Now you should be done! Try running the code! Thanks for reading.

Before you leave, make sure to add me on Linkedin at https://www.linkedin.com/in/krish-lulla-8237a3193/

--

--