Our new React 2.0 framework is now available in PubNub docs. This framework provides tools to leverage our JavaScript SDK and can be used to add real-time capabilities to any React or React Native application.
By the end of this tutorial, you will have a fully functioning mobile chat app that allows multiple users to exchange messages in a chat room. When you open the app, you’ll choose an emoji avatar that will represent your user and then we’ll go straight into a chat room to start chatting.
The full GitHub repo for this project is available here.
Before we begin, there are a few prerequisites for the project.
Node.js (10+) - make sure that you have a recent version of Node installed. If you are not sure, just type into your terminal `node --version`
.
Expo - this tool allows you to quickly prototype a React Native app and test it immediately on your phone (similar to the Create React App). The command below will install the Expo CLI application used to bootstrap and run React Native projects.
npm install -g expo-cli
PubNub Account - to run this app you must create an account with PubNub and obtain your publish and subscribe keys. If you don't already have an account, you can create one for free.
First, we need to initialize a new React Native project using the Expo CLI. This command initializes an empty application. When it asks you what template to use, choose Blank. After that, it will proceed with installing all necessary dependencies and creating the projects structure.
expo init --name PubNubTutorial -t blank
Next, we’ll enter the project directory and install the PubNub Javascript & React SDKs and other helpful packages. Additionally we will be using react-navigation for our navigation needs and react-native-emoji-selector
to simplify things. For more information check out official React Navigation documentation.
cd PubNubTutorial npm install pubnub pubnub-react react-native-emoji-selector @react-navigation/native @react-navigation/stack
Finally, we’ll install the gesture management library for React Native. This library makes touch interactions and gesture tracking not only smooth, but also dependable and deterministic.
expo install react-native-gesture-handler
Now it’s time to create our App component that contains all of our providers and views. We will create a PubNub client instance and pass it to our PubNubProvider to make it easier to use it later. Make sure you add the publish and subscribe keys for your app from your PubNub Account. We also create a stack navigator that we will be using in our app.
Finally, we create our App component that contains all of our providers and views. Make sure to pass our PubNub instance to the PubNubProvider!
In your favourite editor open the App.js
file and replace it’s contents with the code below.
import "react-native-gesture-handler"; import React from "react"; import { NavigationContainer } from "@react-navigation/native"; import { createStackNavigator } from "@react-navigation/stack"; import PubNub from "pubnub"; import { PubNubProvider } from "pubnub-react"; import { EmojiPickerView } from "./views/EmojiPicker"; import { ChatView } from "./views/Chat"; const pubnub = new PubNub({ subscribeKey: "<YOUR-SUBSCRIBE-KEY>", publishKey: "<YOUR-PUBLISH-KEY>" }); console.disableYellowBox = true; const Stack = createStackNavigator(); export default function App() { return ( <NavigationContainer> <PubNubProvider client={pubnub}> <Stack.Navigator headerMode="none"> <Stack.Screen name="EmojiPicker" component={EmojiPickerView} /> <Stack.Screen name="Chat" component={ChatView} /> </Stack.Navigator> </PubNubProvider> </NavigationContainer> ); }
We’ll import two views that we will be using in this tutorial. We haven't created them yet -- we will be doing that in the next steps.
Let’s create a new directory called views
in our project.
Then we can move on to creating the EmojiPicker.jsx
file in the directory. This code is pretty simple. It shows a list of emojis using the emoji selector library. The user can pick an emoji that represents them in the app. The code also includes default styling that can be customized to your liking.
import React, { useState } from "react"; import { StyleSheet, Text, SafeAreaView, Button, View } from "react-native"; import EmojiSelector from "react-native-emoji-selector"; export const EmojiPickerView = ({ navigation }) => { // In here we are soring our currently picked emoji. const [chosenEmoji, setEmoji] = useState(null); // This method will be called when our user selects an emoji const handleEmojiSelected = emoji => { setEmoji(emoji); }; // This method will be called when our user wants to continue with // currently selected emoji - this method will do nothing if user // didn't pick an emoji. const handleContinueButton = () => { if (chosenEmoji !== null) { navigation.replace("Chat", { emoji: chosenEmoji }); } }; return ( <SafeAreaView style={styles.container}> <View style={styles.topContainer}> <Text style={styles.hint}> Pick an emoji that will represent you in the chat! </Text> <View style={{ ...styles.emojiContainer, ...(chosenEmoji === null ? styles.empty : {}) }} > <Text style={styles.emoji}>{chosenEmoji || ""}</Text> </View> <Button // If user haven't chosen an emoji, we disable the continue button disabled={chosenEmoji === null} style={styles.continueButton} title="Continue" onPress={handleContinueButton} /> </View> <View style={{ height: "50%" }}> <EmojiSelector onEmojiSelected={handleEmojiSelected} /> </View> </SafeAreaView> ); }; const styles = StyleSheet.create({ container: { flexDirection: "column", alignItems: "center", width: "100%", height: "100%" }, topContainer: { flexDirection: "column", alignItems: "center", justifyContent: "center", width: "100%", height: "50%" }, hint: { fontSize: 16, textAlign: "center", marginTop: 32 }, continueButton: { marginVertical: 64, width: 300 }, emojiContainer: { width: 64, height: 64, marginVertical: 32 }, emoji: { width: "100%", height: "100%", textAlign: "center", textAlignVertical: "center", fontSize: 60 }, empty: { borderWidth: 5, borderStyle: "dashed", borderColor: "rgba(0, 0, 0, 0.2)" } });
Our last view contains all of the logic of our application; let’s get started! Create a Chat.jsx file in the views directory and copy the code below. This view sets the selected emoji as the avatar for the user using the pubnub.setUUID()
operation. It also subscribes to the chat channel and creates a listener that will add new incoming messages to our message state.
import React, { useEffect, useState } from "react"; import { StyleSheet, Text, View, Button, SafeAreaView, TextInput, KeyboardAvoidingView, Platform, Keyboard } from "react-native"; import { usePubNub } from "pubnub-react"; export const ChatView = ({ route }) => { // The `route` prop will be bassed to us thanks to React Navigation. // It will contain our emoji in `route.params.emoji`. const userEmoji = route.params.emoji; // Here we obtain our PubNub instance thanks to using the provider const pubnub = usePubNub(); // In next two statements we define the state needed for our chat const [input, setInput] = useState(""); const [messages, setMessages] = useState([]); // First we need to set our PubNub UUID and subscribe to chat channel. // We will use `useEffect` hook for that. useEffect(() => { // We need to make sure that PubNub is defined if (pubnub) { // Set the UUID of our user to their chosen emoji pubnub.setUUID(userEmoji); // Create a listener that will push new messages to our `messages` variable // using the `setMessages` function. const listener = { message: envelope => { setMessages(msgs => [ ...msgs, { id: envelope.message.id, author: envelope.publisher, content: envelope.message.content, timetoken: envelope.timetoken } ]); } }; // Add the listener to pubnub instance and subscribe to `chat` channel. pubnub.addListener(listener); pubnub.subscribe({ channels: ["chat"] }); // We need to return a function that will handle unsubscription on unmount return () => { pubnub.removeListener(listener); pubnub.unsubscribeAll(); }; } }, [pubnub]);
Now, we should be ready to receive messages in the chat room. We will walk through the logic to publish a message next.
In the same Chat.jsx
file, add a method to handle message input. This method calls the pubnub.publish()
action to publish a message from the user to the chat channel. This message will be received by all active users in that chat room.
// This function handles sending messages. const handleSubmit = () => { // Clear the input field. setInput(""); // Create the message with random `id`. const message = { content: input, id: Math.random() .toString(16) .substr(2) }; // Publish our message to the channel `chat` pubnub.publish({ channel: "chat", message }); };
Now we need to return our view and add some styling to it. The code below will render the UI to display messages in a list and display a text input to enter messages.
return ( <SafeAreaView style={styles.outerContainer}> <KeyboardAvoidingView style={styles.innerContainer} behavior="height" keyboardVerticalOffset={Platform.select({ ios: 78, android: 0 })} > <View style={styles.topContainer}> {messages.map(message => ( <View key={message.timetoken} style={styles.messageContainer}> <View style={styles.avatar}> <Text style={styles.avatarContent}>{message.author}</Text> </View> <View style={styles.messageContent}> <Text>{message.content}</Text> </View> </View> ))} </View> <View style={styles.bottomContainer}> <TextInput style={styles.textInput} value={input} onChangeText={setInput} onSubmitEditing={handleSubmit} returnKeyType="send" enablesReturnKeyAutomatically={true} placeholder="Type your message here..." /> <View style={styles.submitButton}> {input !== "" && <Button title="Send" onPress={handleSubmit} />} </View> </View> </KeyboardAvoidingView> </SafeAreaView> ); }; const styles = StyleSheet.create({ outerContainer: { width: "100%", height: "100%" }, innerContainer: { width: "100%", height: "100%" }, topContainer: { flex: 1, width: "100%", flexDirection: "column", justifyContent: "flex-end", paddingHorizontal: 16 }, messageContainer: { flexDirection: "row", marginTop: 16, alignItems: "center", backgroundColor: "#fff", padding: 8, borderRadius: 4 }, avatar: { width: 38, height: 38, borderRadius: 50, overflow: "hidden", marginRight: 16 }, avatarContent: { fontSize: 30, textAlign: "center", textAlignVertical: "center" }, messageContent: { flex: 1 }, bottomContainer: { width: "100%", flexDirection: "row", alignItems: "center", padding: 16 }, textInput: { flex: 1, backgroundColor: "#fff", borderRadius: 4, padding: 16, elevation: 2 }, submitButton: { position: "absolute", right: 32 } });
And that’s it! That’s all the code you need. You can now start the project to render the application in your local environment.
Start the app
To start the application, just run npm start in your projects directory and follow the on-screen instructions. If you have an Android or iOS emulator installed, simply press the corresponding letter in the terminal (a for Android and i for iOS). If you want to test on a real device, download the Expo client on your phone and scan the QR code printed in the console to start the app.
In this tutorial, we learned how to create a simple chat app with React Native using PubNub. You’ve now got a basic mobile chat application allowing users to send and receive messages in real time.
Go to our React Reference Docs to learn how to add more features to your app like message persistence, presence, etc.
There are common underlying technologies for a dating app, and in this post, we’ll talk about the major technologies and designs...
Michael Carroll
How to use geohashing, JavaScript, Google Maps API, and BART API to build a real-time public transit schedule app.
Michael Carroll
How to track and stream real-time vehicle location on a live-updating map using EON, JavaScript, and the Mapbox API.
Michael Carroll