[React + Node.js]: Implement Web Socket with Socket.io
React
Node.js
03/20/2020
Websocket
Typical approach of Node.js application requires client to request data, and the server sends the response back--this is HTTP protocol, but what if you want to notify a client when there's a change on the server? Imagine a messenger application. Your friend needs to be able to receive your message in real-time without having to request something to the server. This is where websocket comes into play. Built over HTTP, websockets enable two-way communication where the server can push data to the client without receiving request. Socket.io is a popular library that you can use to establish websocket.
Overview
As an example, I'll be using this MERN stack app to implement websocket. As user adds/deletes an entry, the updated data will be updated in real-time on other browsers as well. The complete project is available here.
Socket.io on the server
Install socket.io
on the server side
npm i socket.io
Create a utility to initialize & get io
socket.js
let iomodule.exports = { init: httpServer => { io = require("socket.io")(httpServer) return io }, getIO: () => { if (!io) { throw new Error("Socket.io is not initialized") } return io },}
Creating socket io connection
app.js
// ...// DB connectionmongoose .connect(MONGODB_URI) .then(result => { const port = process.env.PORT || 8080 const server = app.listen(port, () => { console.log(`Listening on port ${port}...`) })
// Create socket io connection const io = require("./util/socket").init(server) io.on("connection", socket => { console.log("Client connected") }) }) .catch(err => { // Handle error })
Check the import directory of socket util
Sending message to client on adding user
controllers/user.js
const User = require("../models/user")const io = require("../socket")
// ...
exports.postUser = async (req, res, next) => { try { let occupation = req.body.occupation if (!occupation) occupation = "unemployed" const user = new User({ name: req.body.name, age: req.body.age, occupation: occupation, }) await user.save()
// Sends message to all connected users io.getIO().emit("user event", { action: "add", user: { ...user._doc }, })
res.status(201).json({ message: "User created", user: user, }) } catch (err) { if (!err.statusCode) { err.statusCode = 500 } next(err) }}
Sending message to client on deleting user
controllers/user.js
exports.deleteUser = async (req, res, next) => { const userId = req.params.userId try { const user = await User.findById(userId) if (!user) { const error = new Error("Couldn't find the user") error.statusCode = 404 throw error } await User.findByIdAndRemove(userId)
// Sends message to all connected users io.getIO().emit("user event", { action: "delete", userId: userId, })
res.status(200).json({ message: "User removed" }) } catch (err) { if (!err.statusCode) { err.statusCode = 500 } next(err) }}
Client side
Install socket.io-client
on the client side
npm i socket.io-client
Create socket connection & Update state with received data
front-end/App.js
import React, { useState, useEffect } from "react";import openSocket from "socket.io-client"; import User from "./user";import UserAddForm from "./user-add-form";import "./App.css";
const App = () => { const [addLoading, setAddLoading] = useState(false); const [deleteLoading, setDeleteLoading] = useState(false); const [users, setUsers] = useState([]);
useEffect(() => { fetchUsers(); createSocketConnection(); }, []);
const createSocketConnection = () => { const socket = openSocket(process.env.REACT_APP_FETCH_URL); socket.on("user event", data => { if (data.action === "add") { setUsers(prevUsers => [data.user, ...prevUsers]); } else if (data.action === "delete") { setUsers(prevUsers => prevUsers.filter(user => user._id !== data.userId) ); } }); };
const fetchUsers = async () => { ... }; const handleAdd = async formData => { ... }; const handleDelete = async userId => { ... }; return ( ... );};
user event
listens to the event that was sent from the server