Horje
How to Integrate WebSockets with React Redux

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. foldernamemove 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




Reffered: https://www.geeksforgeeks.org


ReactJS

Related
Implement Drag and Drop using React Component Implement Drag and Drop using React Component
How to connect the components using Connect() in React Redux How to connect the components using Connect() in React Redux
Explain the process of submitting forms and handling form submissions in Redux Explain the process of submitting forms and handling form submissions in Redux
Implementing Drag and Drop Functionality with useRef Implementing Drag and Drop Functionality with useRef
How to use Inline Styles in React ? How to use Inline Styles in React ?

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
13