Open In App

How to Integrate WebSockets with React Redux

Last Updated : 04 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Integrating WebSockets with Redux allows for real-time bidirectional communication between the client (e.g. a web browser) and the server. This enables applications to push data from the server to the client instantly, facilitating features such as real-time updates, live notifications, and chat applications

Steps to Setup the Backend:

Step 1: Create a new directory for your project and navigate into it in your terminal.

mkdir server
cd server

Step2: Run the following command to initialize a new Node.js project and create a package.json file:

npm init -y

Step 3: Install web socket Dependencies from the given command.

npm install ws

The updated dependencies in package.json file will look like.

"dependencies": {
"ws": "^8.16.0"
}

Example: This example used to setup the backend for the project.

JavaScript
// index.js
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
    ws.on('message', function incoming(message) {
        console.log('Received:', message);
        // Echo back the received message
        ws.send(message);
    });
});

Output: Run the server with the following command in the terminal

node index.js

Steps to Setup the Frontend

npx create-react-app foldername

Step 2: After creating your project folder i.e. foldername, move to it using the following command:

cd foldername

Step 3: Install required dependencies

npm install react-redux redux redux-thunk

Step 4: After setting up react environment on your system, we can start by creating an App.js file and create a directory by the name of components in which we will write our desired function.

Project Structure:

Screenshot-2024-03-22-215520

The updated dependencies in package.json file will look like.

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"web-vitals": "^2.1.4"
},

Approach to integrate WebSocket with React Redux:

  • Configure Redux store with middleware like redux-thunk or redux-saga for handling asynchronous actions, including WebSocket interactions.
  • Create a WebSocket instance for bidirectional real-time communication between the client and server.
  • Create action creators for WebSocket events, such as sending messages to the server and handling incoming messages.
  • Define reducers to update the Redux store based on WebSocket actions, ensuring state predictability and synchronization with server data.
  • Dispatch WebSocket actions from components or middleware to initiate WebSocket communications and reflect real-time updates in the UI through Redux-managed state.

Example: Implementation to showcase the process integrating WebSockets with Redux using chat application.

JavaScript
//src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { thunk } from 'redux-thunk';
import rootReducer from './reducers';
import App from './App';
import { connectWebSocket } from './actions/websocketActions';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

store.dispatch(connectWebSocket());

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
JavaScript
// App.js

import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import {
    connectWebSocket,
    sendMessage,
    receiveMessage
} from './actions/websocketActions';

const App = ({ connectWebSocket,
    sendMessage, receiveMessage,
    messages }) => {
    const [messageText, setMessageText] = useState('');

    useEffect(() => {
        connectWebSocket();
    }, [connectWebSocket]);

    useEffect(() => {
        const messageChannel =
            new BroadcastChannel('chat_messages');

        messageChannel.onmessage = (event) => {
            // Update messages in this tab 
            // when a message is received from another tab
            receiveMessage(event.data);
        };

        return () => {
            messageChannel.close();
        };
    }, []);

    const handleMessageChange = (e) => {
        setMessageText(e.target.value);
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        if (messageText.trim() !== '') {
            sendMessage(messageText);
            setMessageText('');
        }
    };

    return (
        <div className="App">
            <h1>Real-Time Chat Application</h1>
            <div className="chat-container">
                {messages.map((message, index) => (
                    <div key={index} className="message">
                        {message}
                    </div>
                ))}
            </div>
            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    value={messageText}
                    onChange={handleMessageChange}
                    placeholder="Type your message..."/>
                <button type="submit">Send</button>
            </form>
        </div>
    );
};

const mapStateToProps = (state) => ({
    messages: state.websocket.messages
});

export default connect(mapStateToProps,
    {
        connectWebSocket,
        sendMessage,
        receiveMessage
    })(App);
JavaScript
// websocketActions.js

let ws;
let messageChannel;
let isEventListenerSetup = false;

// Generate a unique identifier for each browser
const userId = Math.random().toString(36).substring(7);

export const connectWebSocket = () => (dispatch) => {
    // Ensure WebSocket connection is established only once
    if (!ws || ws.readyState === WebSocket.CLOSED) {
        ws = new WebSocket('ws://localhost:8080');
        ws.onopen = () => {
            console.log('WebSocket connected successfully!');
        };

        ws.onmessage = async (event) => {
            const message = await event.data.text();
            const formattedMessage = `${userId}: ${message}`;

            // Broadcast the received message 
            // to all Broadcast Channel clients
            messageChannel.postMessage(formattedMessage);
        };

        ws.onerror = (error) => {
            console.error('WebSocket error:', error);
        };

        ws.onclose = () => {
            console.log('WebSocket connection closed.');
        };

        // Log the WebSocket object to check 
        // if it's being created multiple times
        console.log('WebSocket:', ws);
    }

    if (!messageChannel) {
        messageChannel = new BroadcastChannel('chat_messages');
    }

    if (!isEventListenerSetup) {
        messageChannel.onmessage = (event) => {
        };
        isEventListenerSetup = true;
    }
    dispatch({
        type: 'WEBSOCKET_CONNECTED',
        payload: ws
    });
};

export const sendMessage = (message) => (dispatch) => {
    if (ws && ws.readyState === WebSocket.OPEN) {
        ws.send(message);
    }
};

export const receiveMessage = (message) => ({
    type: 'WEBSOCKET_MESSAGE_RECEIVED',
    payload: message
});
JavaScript
// WebSocketComponent.js

import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { connectWebSocket } from '../actions/websocketActions';

const WebSocketComponent = ({ connectWebSocket, messages }) => {
    useEffect(() => {
        const socket = connectWebSocket();

        socket.onopen = () => {
            console.log('WebSocket connected successfully!');
        };

        socket.onerror = (error) => {
            console.error('WebSocket error:', error);
        };

        socket.onclose = () => {
            console.log('WebSocket connection closed.');
        };

        return () => {
            socket.close();
        };
    }, [connectWebSocket]);

    return (
        <div>
            {messages.map((message, index) => {
                console.log('Message:', message);
                return (
                    <div
                        key={index}
                        style={{
                            padding: '5px 10px',
                            margin: '5px',
                            borderRadius: '5px',
                            alignSelf: message.source ===
                                'right' ? 'flex-end' : 'flex-start',
                            backgroundColor: message.source ===
                                'right' ? '#d3d3d3' : '#f0f0f0',
                        }}>
                        {message.content}
                    </div>
                );
            })}
        </div>
    );
};

const mapStateToProps = (state) => ({
    messages: state.websocket.messages,
});

export default connect(mapStateToProps,
    { connectWebSocket })(WebSocketComponent);
JavaScript
//src/store/configureStore.js

import { createStore, applyMiddleware } from 'redux';
import { thunk } from 'redux-thunk';
import rootReducer from '../reducers';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

export default store;
JavaScript
// reducers/websocketReducer.js

const initialState = {
    connection: null,
    messages: []
};

const websocketReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'WEBSOCKET_CONNECTED':
            return {
                ...state,
                connection: action.payload
            };
        case 'WEBSOCKET_MESSAGE_RECEIVED':
            return {
                ...state,
                messages: [...state.messages, action.payload]
            };
        case 'WEBSOCKET_MESSAGE_SENT':
            return {
                ...state,
                messages: [...state.messages, `Sent: ${action.payload}`]
            };
        default:
            return state;
    }
};

export default websocketReducer;
JavaScript
// src/reducers/index.js

import { combineReducers } from 'redux';
import websocketReducer from './websocketReducer';

export default combineReducers({
    websocket: websocketReducer
});

Steps to run the Application:

npm start

Output: Your project will be shown in the URL http://localhost:3000/

cc



Similar Reads

How to Integrate Redux with React Components ?
Redux is an open-source JavaScript library for managing and centralizing application state. It helps you to write applications that behave consistently and are easy to test and run in different environments. It can also be understood as the predictable state container for the JavaScript app. It is most commonly used with JavaScript libraries like R
4 min read
Benefits of using React-Redux over plain Redux
React-Redux streamlines state management in React applications by integrating seamlessly with Redux, eliminating prop drilling. It enhances predictability and scalability through centralized state management and facilitates the creation of reusable UI components. This integration optimizes syntax, approaches, and implementation steps, offering a ro
5 min read
How Redux Toolkit simplifies Redux code in React application ?
Redux Toolkit is a powerful library designed to simplify the complexities of managing application state with Redux in React applications. At its core, Redux Toolkit provides developers with a set of utilities and abstractions that significantly reduce boilerplate code and streamline common Redux tasks. This means that developers can focus more on b
5 min read
Real-time Updates with WebSockets and React Hooks
The WebSocket protocol provides continuous, real-time, full-duplex communication between a client and server over a single TCP socket connection. The WebSocket protocol has only two plans: opening a handshake and facilitating data transfer. After the server receives the handshake request sent by the client and initiates a WebSocket connection, it c
5 min read
Real-Time Collaborative Editing App using React & WebSockets
This article will explain about real-time collaborative editor which can be updated by multiple users at the same time. It explains how to do it using a web socket for example - socket.io and react hooks such as useState() and useEffect(). Output Preview: Let us have a look at how the final output will look like. [caption width="800"]OUTPUT PREVIEW
4 min read
Why Redux Toolkit is preferred over Redux ?
While working as a Front-end Developer or Full Stack Developer, many engineers encountered Redux. But Recently Redux Team launched Redux Toolkit(RTK), an officially recommended SOPE library that stands for Simple, Opinionated, Powerful, and Effective state management library. It allows us to write more efficient code, speed up the development proce
4 min read
Redux Toolkit Better way to write Redux code in ReactJS
Redux Toolkit is used for writing redux code but in a more concise way. Redux Toolkit (RTK) solves three bigger problems that most of the developer's face who used redux in a react application. Too much code to configure the store.Writing too much boilerplate code to dispatch actions and store the data in the reducer.Extra packages like Redux-Thunk
4 min read
Redux Thunk vs. Redux Saga: Choosing the Right Middleware
Redux, a predictable state container for JavaScript apps, is widely used in React applications for managing application state. Redux Thunk and Redux Saga are middleware libraries commonly employed with Redux for managing side effects such as asynchronous API calls, complex state transitions, and more. Both offer solutions for handling asynchronous
3 min read
How does Redux Saga Differ from Redux Thunk ?
In Redux, middleware plays a crucial role in managing asynchronous actions. Redux Saga and Redux Thunk are two popular middleware libraries used for handling side effects in Redux applications. While both serve similar purposes, they differ significantly in their approach and capabilities. In this article, we'll see the differences between Redux Sa
5 min read
How does Redux Toolkit simplify Redux development?
Redux is a powerful state management library for JavaScript applications, but setting it up and managing boilerplate code can be cumbersome. Redux Toolkit is an official package from the Redux team designed to streamline Redux development and reduce boilerplate code. In this article, we'll explore how Redux Toolkit simplifies Redux development and
6 min read
Article Tags :