This tutorial will show you how to build a messaging app using React Native and Firebase. First, we’ll set the stage for this tutorial by understanding why you should use React Native in the first place. Then we’ll explain React Native Hooks functions before diving into the implementation details of how to build a chat application with React Native and Firebase.
Please note that this tutorial assumes a basic understanding of React Native.
With this in mind, let’s begin.
Most applications today are built natively, but there are also hybrid applications developed using Webviews. The properties and features of Webviews introduce limits to applications in comparison to their native counterparts, however. Applications using Webviews are also relatively slower than native applications. React Native allows to bridge this gap by providing applications access to native functionality and properties.
Because of its advantages of code reusability, reliability, robustness, and the existence of a helpful community, many companies, from established Fortune 500 companies to high-growth startups, develop applications with React Native. Some well-regarded organizations using React Native include Instagram, Shopify, Tableau, Tesla, UberEats, and Skype.
Now that we have understood the basics let’s move on to React Native Hooks functions.
Hooks are functions that let you “hook” into React states and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes. (We don’t recommend rewriting your existing components overnight, but you can start using Hooks in the new ones if you’d like.)
React provides a few built-in Hooks such as useState. You can also create your own Hooks to reuse stateful behavior between different components. Let’s look at the built-in Hooks first.
For more information about Hooks, check out the react site.
Now let’s understand how to work with Sendbird and Hooks for your chat application. This tutorial is composed of nine more parts:
You need to have the React Native CLI to work with this example. Please install globally:
npm install -g react-native-cli
You can quickly check that the installation finished correctly by showing the version number:
Once you have the React Native CLI installed, you can download the sample code. First clone the repository:
Then, open the following folder using your favorite IDE:
Next step, let’s install all the dependencies. Run the following command: (it takes some time to download all packages)
You can run this either for Android or iOS. Open the package.json file and check the scripts we have for you to use:
To run Android, just type:
npm run android
If you have problems running the Android version, you can try the following:
export ANDROID_HOME=/Users/[your user]/Library/Android/sdk
For iOS, you need to install Pods once:
cd ios pod install pod update
This will install all the libraries to run the iOS version.
If you have problems running the iOS version, you can try the following:
Check the list of prerequisites before running this sample. You can find them here.
From your iOS simulator, you can debug the application and see console outputs to Chrome. Press Command + D to see a screen like below:
When you click Debug in Chrome, a Chrome window will open. Use the Chrome developer tools to monitor the outputs written by console.log().
The following is a description of the most important files and folders for this sample application.
This file contains all the packages this project will use. Because of compatibility issues, we recommend not changing the version of these packages.
The file also contains the scripts you can run:
Run npm run android to launch the Android version.
Run npm run ios to launch the iOS version.
Run npm run clean to clear any previous build.
This folder contains all the downloaded libraries this project will use. This folder is created every time you run npm i.
This folder contains all the files for an Android project. You can use Visual Studio to open and run the project.
This folder contains all the files for the iOS project. You can open it with Xcode.
This is the first file to be executed by React Native.
We define which function will process the information when we receive a Push message, for Android devices. In this example, the function onRemoteMessage will do the work. It is inside the ./src/utils utils.js file.
This file is called from the file index.js. It shows the first screen we see when users run our application.
You will see where your Sendbird application ID (you can get this from your Sendbird Dashboard) is defined:
We are now ready to initialize the Sendbird SDK. At this point, we are not connected.
We will use our first Hook to ask permission to receive notifications.
If we already have a token from Firebase, we use the Sendbird function registerAPNSPushtokenForCurrentUser to register it for an iOS device and registerGCMPushTokenForCurrentUser for an Android device.
A detailed tutorial about implementing Firebase for React Native is available here.
The next step is to define our Stack Navigator. A Stack Navigator provides a way for your app to transition between screens. Each new screen can be placed on top of a stack.
By default, the stack navigator offers a familiar iOS and Android look and feel. New screens slide in from the right on iOS, and the default OS animation is used on Android. You can customize the animations to match your needs.
The Stack Navigator will Lobby first. This file is in ./src/page/lobby.js.
Many Hooks are applied here, and the principal actor is currentUser. Because it depends on the user previously signed, we will show a login screen or the list of chat channels.
This sample uses AsyncStorage to read (and save) the value of a previously signed user. If there’s a value, we read it and define that the user as initialized.
We build the screen accordingly. If we have a signed user, we show the Channels component. Otherwise, we show the Login screen.
Once the Login component responds, the login function executes. We save this value in the AsyncStorage and run the token registration for iOS and/or Android to receive push notifications.
The login screen will paint the screen we see above and wait for the user to click on the Connect button.
When clicked, the button calls the connect function connect.
As you can see, the sendbird.connect(…) function call connects the device with the Sendbird servers via Websockets. If you see any error here, it is because the user ID selected is invalid, your Sendbird application ID is incorrect, the function is not defined, or because you’re not connected to the Internet.
We suggest you check for any error code and inform your users.
You must check if your application goes to the background, for the following screens (channels, messages, etc.). If this happens, then you should invoke setBackgroundState. You will appear as disconnected for the rest of the users. Call setForegroundState once the application returns to the front. This will make Websockets connect again and the logged user appear online again.
After a successful connection, we store this user’s nickname (from the input screen) and return it to the Lobby component. Once the user is set, the Channels screen appears.
Sendbird works with public Open Channels and Group Channels. You don’t need an invitation, any user can enter a message, for the Open Channels. Group Channels are the opposite. You need an invitation to join the channel and send messages.
Now, open the ./src/page/channels.js file and see all the essential parts that should be there.
Customers must know when the SDK is not connected because your application should stop all requests until a connection is made. Otherwise, your users will think that your application is not working.
A channel handler helps your application to update visually and inform the customer about any changes made by other users.
Also, remember to remove your handlers when destroying each view.
Read more about connection and channel handlers here.
When listing channels, the maximum number of records you can receive from the server is 100. If you need more, you must use next(…) to call the server again:
Once the user selects a channel, our application will navigate to the chat, passing the selected channel and current signed user.
This leads us to the messages screen.
After selecting a channel, the application will show you a list of messages for this channel only:
The code for this screen is in ./src/page/chat.js.
Naturally, the list of messages is the most important part of this screen. But let’s take a look at other parts that must be included:
You need to remove these handlers when this view is no longer active for users.
In this sample application, we will take action for the following events:
onMessageReceived: A new message is received via Websocket. Add this new message to the list of messages only if the channel URL informed from the event is the same channel you have active in this view.
onMessageUpdated: A message has been updated. If this informed message belongs to this active channel, you must update its content from the list.
onMessageDeleted: A message has been deleted. If this informed message belongs to this active channel, you must remove it from the list.
You can also receive events about to this user and the channel. For example:
onUserLeft: If a user left the channel, you should check if the signed user ID matches the informed user ID. If both are the same, it means that you decided to leave the channel from another device. Your action should be something like going back to the main list of channels (channels.js).
onChannelDeleted: Another critical check is the existence of the channel that contains the messages you are showing. If this channel is missing for any reason, you should also return to the main screen with the list of remaining channels (channels.js).
When you enter to see the messages in a channel, you should mark all messages as read.
We will ask Sendbird for the list of messages in this active channel. To do this, we use createPreviousMessageListQuery
For this example, we list the last 50 messages. If more messages are available to show, you should call next() to get more data.
You also can request a list of messages according to a specific timestamp. Use something like new Date().getTime() and define how many previous messages you want to show.
This technique will help you load previous messages if the user scrolls up to get a long history of messages. You just need to keep sending the message’s timestamp and ask for previous records to show until you reach the first message sent to the channel.
To find more information, check our docs.
With Sendbird, you can send a text or a file message.
Note that a message for Sendbird is not just a text or a file; we have other attributes to send confidential information to your server or to the other connected users.
This attribute is part of the message object, and it specifies a custom message type for message grouping. The length is limited to 128 characters.
The Sendbird Advanced Analytics also uses Custom types to segment metrics. They enable the sub-classification of data views.
Data is another attribute you can use for your internal logic. It specifies additional message information such as custom font size, font type, or any data you want to handle with a JSON formatted string.
To find a list of attributes for a message, click here.
Below is the code to send a message.
With Sendbird, you can also send file messages. In this application, we allow users to select the file from the device and then send it.
When working with Android and iOS, you must request permission from users before using a file picker.
Remember that when sending file messages, you cannot send text along with it. However, you can use other message object attributes to send a text and display it on the chat list. Check out the list of available attributes for a file message:
More information is in the docs.
When uploading image files, you can ask Sendbird to generate a thumbnail automatically. Enable this from your dashboard:
You can send voice messages with file messages. Your frontend application should implement a voice recording function and save the result as a file. Then you just upload that file, along with any of the custom attributes we provide for the message object, indicating that this is a voice message file. When drawing this message in the channel, analyze the information and show it properly.
Sendbird supports message threading so you can respond to messages.
To do this, you must indicate the parent message ID when sending a response.
You can respond with a text message:
Or with a file message:
Sendbird offers a variety of features for your chat experience.
In this example, you can see the use of startTyping() and endTyping() to send events to all members in the channel.
This event triggers the onTypingStatusUpdated function from the channel handler.
The rest is to show a label saying that user XXX is typing a message.
Sendbird provides powerful tools for moderation. You can use the Sendbird Dashboard to moderate messages and users. You may also register or unregister channel members as Operators so that they can freeze channels, block, or mute users.
If you have any questions regarding this tutorial or using Sendbird in general, please drop us a note on our community page here.
Thanks, and happy React Native chat building!
Stay up-to-date on the latest technical content from Sendbird.