The web was originally implemented on the principle of request and response: a client sends a request and a server replies with an appropriate response. When there is no requirement for data manipulation according to a client request, it’s customary to serve a static page.
When dynamic pages came along, we were introduced to the concept of GET, POST, PUT, and DELETE requests. Now a client is able to ask for custom data from a server by sending its requirements as parameters. The server then processes the request and returns a dynamic response.
Still, the fundamentals are based on request and response: the client requests, the server responds, and the connection is closed.
There is another protocol that doesn’t work like a request-response scheme known as WebSockets. In this tutorial, we’ll introduce you to the WebSockets technology and some of its common use cases. We will walk you through how to implement WebSockets in React Native by designing a message broadcast app.
WebSockets is a protocol that provides full-duplex communication, which means the client and server stay connected over a single TCP connection. Unlike request-response communication, the connection doesn’t close here.
Since it’s full-duplex, the server can also send data to the client without a request. This helps in saving bandwidth and response time.
The WebSockets protocol starts with ws://
. For dealing with hypertext, we use http://
or https://
.
There is a range of applications for WebSockets, but we primarily use them in cases where data needs to be either broadcasted or pushed by server.
For example, a chat app server needs to send a message to a recipient as soon as the sender sends it. The server can’t wait for the client to request new messages, so it pushes them to the client using full-duplex communication. Similarly, news, trade matrix, and even social media posts are pushed in this way.
This outcome could be achieved before WebSockets entered the picture, but they were unreliable and inefficient.
For instance, many companies used long polling in which a browser sends a request but the server does not respond. The connection stays active until there is a connection timeout. In the meantime, if server has any message for the client, it responds and the connection closes. As soon as the connection closes, either due to timeout or server response, the client again sends the request. This process keeps going.
Another approach is to send an AJAX request in an interval of few seconds — say, three seconds — and get a server response, which is either a valid message or empty object. So, a new message can get delayed for, at most, three seconds. The downside of this approach is that a lot of API calls go to waste because the server doesn’t have any message to respond to.
There are four main functions that are executed during the lifecycle of WebSockets. They are called when the app establishes a connection, receives a messages, catches an error, and disconnects.
Let’s zoom in on these functions.
The first step is to establish a connection with the server. WebSockets work on their own protocol, ws://
.
In React Native, we can create a connection using the following code:
var ws = new WebSocket('ws://host.com/path');
Here the link corresponds to the socket service running on the backend.
If the server accepts the connection, it notifies the client by invoking the onopen
function over the WebSocket object:
ws.onopen = () => { // connection opened ws.send('something'); // send a message };
This acts like the constructor. You can allocate resources in this function. Also, if you need to send some one-time information, such as a user identification number, to the server, you can do so here.
In a request-response policy, the client looks for a response in the sent request. This means the client knows when it will get data from the server so it stays ready to handle it. But in full-duplex communication like WebSockets, the server can send data at any time without the consent of the client.
To handle this situation, a function must be invoked when the server sends a message.
In React Native, we have an onmessage
function for this:
ws.onmessage = (e) => { // a message was received console.log(e.data); };
When there is an error, either due to poor internet connectivity or internal server errors, the onerror
function is called:
ws.onerror = (e) => { // an error occurred console.log(e.message); };
When the connection closes, either by the server or by the client, the onclose
function is called. This acts like a destructor in that you can release the allocated resources.
ws.onclose = (e) => { // connection closed console.log(e.code, e.reason); };
Now we’ve seen the complete lifecycle of WebSockets in React Native, from establishing a connection to closing it. We can write all these functions in single block as follows:
var ws = new WebSocket('ws://host.com/path'); ws.onopen = () => { // connection opened ws.send('something'); // send a message }; ws.onmessage = (e) => { // a message was received console.log(e.data); }; ws.onerror = (e) => { // an error occurred console.log(e.message); }; ws.onclose = (e) => { // connection closed console.log(e.code, e.reason); };
To show WebSockets in action, let’s create a simple message broadcasting app in Rect Native. In our demo app, a message sent from one application will be broadcast to all connected applications.
We’ll develop the server script in Node.js. Here’s the WebSockets server code:
const express = require("express"); const app = express(); const http = require("http"); const WebSocket = require("ws"); const server = http.createServer(app); const wss = new WebSocket.Server({ server }); wss.on("connection", function connection(ws) { ws.on("message", function incoming(message, isBinary) { console.log(message.toString(), isBinary); wss.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send(message.toString()); } }); }); }); app.get("/", (req, res) => { res.send("Hello World!"); }); server.listen(8080, () => { console.log("Listening to port 8080"); });
You can find this code in the npm repositoriy for the ws package.
Remember, you need to keep the server active. Otherwise, clients won’t be able to connect.
Now that our server is running successfully, it’s time to create our React Native app.
At the top, we’ll create a horizontal bar to show connection or disconnection notifications as well as errors. At the bottom, we’ll place an input field and a submit button to send messages through WebSockets. The rest of the middle area will be used to display the list of messages received from the server.
It’s a broadcast app, so any message sent from any device will be broadcasted to all.
Let’s check out the code:
import * as React from 'react'; import { Text, View, StyleSheet, TextInput, Button, ScrollView } from 'react-native'; export default function App() { const [serverState, setServerState] = React.useState('Loading...'); const [messageText, setMessageText] = React.useState(''); const [disableButton, setDisableButton] = React.useState(true); const [inputFieldEmpty, setInputFieldEmpty] = React.useState(true); const [serverMessages, setServerMessages] = React.useState([]); var ws = React.useRef(new WebSocket('ws://w567l.sse.codesandbox.io/')).current; React.useEffect(() => { const serverMessagesList = []; ws.onopen = () => { setServerState('Connected to the server') setDisableButton(false); }; ws.onclose = (e) => { setServerState('Disconnected. Check internet or server.') setDisableButton(true); }; ws.onerror = (e) => { setServerState(e.message); }; ws.onmessage = (e) => { serverMessagesList.push(e.data); setServerMessages([...serverMessagesList]) }; }, []) const submitMessage = () => { ws.send(messageText); setMessageText('') setInputFieldEmpty(true) } return ( <View style={styles.container} <View style={{ height: 30, backgroundColor: '#eeceff', padding: 5 }}> <Text>{serverState}</Text> </View> <View style={{ backgroundColor: '#ffeece', padding: 5, flexGrow: 1 }}> <ScrollView> { serverMessages.map((item, ind) => { return ( <Text key={ind}>{item}</Text> ) }) } </ScrollView> </View> <View style={{ flexDirection: 'row', }}> <TextInput style={{ borderWidth: 1, borderColor: 'black', flexGrow: 1, padding: 5, }} placeholder={'Add Message'} onChangeText={text => { setMessageText(text) setInputFieldEmpty(text.length > 0 ? false : true) }} value={messageText} /> <Button onPress={submitMessage} title={'Submit'} disabled={disableButton || inputFieldEmpty} /> </View> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#ecf0f1', paddingTop: 30, padding: 8, }, });
To summarize in brief, the process for implementing WebSockets in a React Native app is as follows:
ws
serverState
variable, the middle part displays a text messages stored in the serverMessages
array, and the bottom bar has an input field that stores typed messages in the messageText
state variabledisableButton
and inputFieldEmpty
variables. The submit button will be active when both disableButton
and inputFieldEmpty
are false
useEffect()
hook, we define all the WebSocket functionsonopen
function is called. This changes the value of serverState
variable to connected to the server
and disableButton
to false
, so the top bar of the app shows the connected messageonclose
function is called. This changes the serverState
to disconnected message
and disableButton
to true
. At this point, the submit button is no longer active, even if you type a message in the input box, because we can’t send messages to the serveronerror
function is called and changes the serverState
to that particular error messageonmessage
function is called. This will append the received message into the serverMessages
arraysubmitMessage
message to send the message to the server, which the server broadcasts to all devicesHere, I embedded two instances of this app: one for Android and another for iOS. You can test that the message sent from one device will be shown on both. Verify that the Node.js server we embedded above is running fine and is not in hibernation mode.
If you encounter problems with the above devices, you can run the code here.
In this tutorial, we showed you how easy it is to create WebSockets in a React Native apps. The code will run on both Android and iOS platforms.
We created a simple broadcast app for this particular demonstration, but there is a lot of scope to expand it. For example, you could set IDs to distinguish between clients and align messages sent by the client to the right side and all others to the left side. This will produce a perfect chat app-like look and feel.
You could also create a form to capture details (such as the user’s name) before starting a chat and show the information beside received messages.
Let us know in comments what kinds of things you’ve built in React Native using WebSockets.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn to build scalable micro-frontend applications using React, discussing their advantages over monolithic frontend applications.
Build a fully functional, real-time chat application using Laravel Reverb’s backend and Vue’s reactive frontend.
console.time is not a function
errorExplore the two variants of the `console.time is not a function` error, their possible causes, and how to debug.
jQuery 4 proves that jQuery’s time is over for web developers. Here are some ways to avoid jQuery and decrease your web bundle size.
2 Replies to "How to implement WebSockets in React Native"
Great article, thanks 🙂
FYI: The JSX is incorrectly escaped in the example code
Thanks for catching that. We’ve updated the post