How to Create a Hybrid Chat App With React.js
In this tutorial we will learn how to create a live chat hybrid app, where people can join a chat group and communicate together (similar to Discord).
The app uses React.js to create the UI, with a help from Framework7 to style components like messages. Redux Toolkit is used as state management (even though Context API would be enough, it is good to learn the fundamentals on small projects before moving on to bigger ones). As for the database, Firebase provides the authentication and the data storage. This article will explain just the main parts of code, styling and basic React code will not be covered.
After finishing the tutorial, you will have a functioning app that can be run on a desktop, as well as on iOS or Android devices (with the help of Monaca deployment).
Link for the repository: Chat App
Terminology
First of all, let’s go through definitions of technologies which will be used.
Redux Toolkit
Most of you probably have experience with React.js and passing data through props. To get around problems as “prop drilling” (passing props multiple layers from parent to child) and have the data globally available in every part of the app, we can use Context API or Redux Toolkit. The later one is meant to be a successor of React Redux, with easier configuration and less boilerplate code to write to set up a Redux store. Nowadays, the Redux team officially recommends Redux Toolkit as a state management solution.
Firebase
“App development platform that helps you build and grow apps and games users love.” Firebase provides multiple cloud solutions, from which we will be using authentication and Firestore database. Since these products are for free, you will not need to buy any subscription.
Monaca
Cross-platform hybrid mobile app development platform and tools in the cloud. In this app, Monaca will serve as a simple solution for deployment on a mobile device. You also have the possibility of development in the cloud with Monaca Cloud IDE, so you do not have to download anything on your computer.
Prerequisites:
Workflow:
- Implement authentication of user.
- Create Redux Toolkit store.
- Add database of chat groups.
- Enable the users to send and receive messages.
- Bonus: Building on Windows, PWA and Firebase deployment.
App Wireframe
Setting up The Project
To simplify the development, we can start from a template provided by Monaca CLI. After creating an account, open the console and write the following commands:
$ npm install -g monaca // install Monaca CLI
$ monaca login // login with your new account
$ monaca create Chat-App // create new projectChoose: Onsen UI and React -> Framework7 React Blank
After this, you should see a new folder “Chat-App” created. Open it in your preferred code editor and we can start coding.
User Authentication
At first, we need to set up Firebase. The process is pretty straightforward, but for detailed explanation, see “Configuring Firebase” section in this article.
After the initialisation, run npm install firebase
command and your project should be connected to the Firebase console.
Now go to Authentication –> Sign-in method –> Add new provider and enable Anonymous option.
Let’s add two functions for login and logout to “db.js” file. The login function is called on button click event in Loading Page. After successful login, user is redirected to Groups Page (see image of wireframe above). Open the DevTools of your preferred browser, go to Application menu and find firebaseLocalStorage. You can see the information of anonymous user (most importantly uid) provided by Firebase. This value will be used to identify the logged in user.
For now, ignore the code on line 14 with store, we will come to that later. On line 16, the Framework7 changes the page to Login page and clears history, therefore the user cannot move back once he/she is logged out.
Redux Toolkit - Store Global State
Here is a simplified version of store creation, based on this tutorial. Codes of the files are provided below.
- Run command
npm install @reduxjs/toolkit react-redux
and create a directory “/store” which will contain all the store logic. Inside that directory, create “/store/slices” directory. - We will use two slices in this app, therefore create “groupsSlice.js” and “userSlice.js”. These slices hold reducer logic and actions for a single feature of app (in this case information about chat groups and the user).
- In “/store”, create “store.js” which will include reducer object with all of our slices.
- Wrap your application by React Redux <Provider> in “app.jsx” to make the store available in the components.
- Use the state with useDispatch() and useSelector() in functional React components, and store.dispatch() and store.getState() in vanilla Javascript files.
Slices always include name for identification, initial state and reducers (which are exported by object destructuring).
In the state of groups (line 3) we have an array of all chat groups saved in the database, but also selected group which user clicked to join, as well as notification if new message was sent/received and the UI should show it.
Adding the slices to the store and wrapping the app with provider:
For debugging the state of the app, install an extension to your browser called Redux DevTools, where you can see in realtime how the state is being changed. After installing, click on the circle icon and the interface will open.
Firestore Database
To store chat groups, users and messages, we will use Firestore Database. Navigate into this database in Firestore and create three collections: groups, messages and users. In each one, create a mock document (let Firestore assign a Document ID automatically). The structure is as follows:
Groups: {category: string, description: string, image: string, name: string}.Messages: {avatar: string, group: string, name: string, text: string, time: number, userID: string}.Users: {gender: string, image: string, name: string}.
The final result should look similar to this:
In the image above you can see a link to a Firebase storage. To store images (in our case a thumbnails of the chat groups), go to Storage, upload your image and copy the link to access it.
Combining Redux Toolkit with Database
After user fills the name and selects gender in the Login page, login function will run and the data will be sent to database. On line 2 below, random user API is used to get an image for user, and on line 7 the login action is dispatched to update state in “userSlice.js”.
Firebase has an onAuthStateChanged function shown below, which listens to changes in authentication state. When the user is logged in, the condition on line 2 is true (this user is created by Firebase with uid we saw in DevTools). We obtain the chat groups from database and upload uid and these groups in Redux store (lines 4 and 5).
Next, we check if the user exists in database (line 14). If yes, we fill in remaining info into “userSlice.js”. If the user does not exist, we add new one to the database (line 25).
The getChatGroupsFirebase function retrieves chat groups from database and returns object with arrays of groups.
Those groups are then displayed in “GroupsPage.jsx”. After user clicks on one of those groups, the selected group’s name is sent to the store to be later displayed in “MessagesPage.jsx”.
Sending and Receiving Messages
After the user is redirected to “MessagesPage.jsx”, useEffect hook will start listening to new messages (line 2). If a new message appears in the database, the component will render the messages (line 6).
The styling and logic of messages is based on Framework7 message template. Since the Message component needs type as a property to decide on which side (left or right) will the message be rendered, we use getTypeOfMessage function (line 11) to compare if the uid of sender is the same as currently logged in user’s uid.
As the last step, we need to implement functions that will send and receive messages from the database.
After getting the messages, we filter them to contain only messages from our chosen group and sort them by the time of creation (lines 7 and 8). Sending is similar as uploading user to database, with the difference of creating a timestamp (line 17). Subscribing to changed in database is done by onSnapshot function provided by Firebase. If a change is made, we dispatch an action so the Message component listening to changes can re-render.
Congratulations! If you followed all the steps above, you should now have a functioning chat app where users can send and receive messages!
PWA, Windows Build and Firebase Deployment
As a bonus tasks, you can:
- Use Windows build to have standalone desktop app.
- Build a PWA - this includes creating
service-worker.js
(registered inindex.html
),manifest.json
and icons in multiple sizes. - Deploy to Firebase - after this you can publicly access the app.
Conclusion
In this tutorial we learnt how to create anonymous authentication in Firebase, save images and data to database, and send and receive messages. Some ideas for future improvement might include authentication via email or social media, or creating private chats between two users.
If you want to deploy and test it on your own mobile device, check this tutorial to guide you through the Monaca deployment process.
In case of any questions or suggestions for improvements, do not hesitate to contact me.
Good luck and have fun coding! :)