How to Integrate WebSockets with React Redux
Last Updated : 04 Apr, 2024
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:

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/
