Enhancing Real-Time Communication in Your React Flask Project with Socket.IO

Enhancing Real-Time Communication in Your React Flask Project with Socket.IO

Real-time communication has become a crucial part of modern web applications, enabling users to interact seamlessly and stay updated with live information. Integrating Socket.IO into your React Flask project can enhance the user experience by providing real-time features like chat, notifications, and updates. In this blog, we'll explore how to implement Socket.IO in a React Flask project to enable real-time communication, offering a more engaging and interactive platform for users. By leveraging Socket.IO's real-time capabilities, you can create dynamic applications that respond instantly to user actions, providing a more immersive experience. In this post, I will be using Socket.IO, paired with Flask-SocketIO, to make a chat feature that not only instantly send the message to both the recipient and sender, but also store the message in the database.

Setting Up Your Flask Server:

First, ensure you have Flask, flask_sqlalchemy, sqlalchemy, and flask_migrate installed in your project by using pip or pipenv:

pip install Flask
pip install flask_sqlalchemy
pip install sqlalchemy
pip install flask_migrate
pip install sqlalchemy_serializer

Next, install Flask-SocketIO for integrating Socket.IO with Flask:

pip install flask-socketio

Now we will build a config.py file to set up the Flask server, the SQLAlchemy integration, and the Socket.IO integration:

# config.py
from flask import Flask, render_template
from flask_socketio import SocketIO
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from flask_migrate import Migrate

app = Flask(__name__)

db = SQLAlchemy(app=app)

migrate = Migrate(app=app, db=db)

socketio = SocketIO(app=app)

api = Api(app=app)

Now let's build a basic models.py to establish the database. In this example, I will only be making a Chat and Message model to drive home the point of this example. Just know that you can also make User models and create relationships between users through the Chat model so the messages will be stored in the database with relationships to the users involved in the chat. Since the example below is only using a Chat and Message model through a one-to-many relationship, the serialize rules for them is a lot more straight forward than if you included the many-to-many between User and User through the Chat model.

# models.py
from sqlalchemy_serializer import SerializerMixin
from config import db

class Chat(db.Model, SerializerMixin):
    __tablename__ = 'chats'

    serialize_rules = ('-messages.chat',)

    id = db.Column(db.Integer, primary_key=True)
    messages = db.relationship(
        'Message',
        back_populates='chat',
        cascade='all, delete-orphan',
        lazy='select'
    )

    def __repr__(self):
        return f'Chat ID {self.id}'

class Message(db.Model, SerializerMixin):
    __tablename__ = 'messages'

    serialize_rules = ('-chat.messages',)

    id = db.Column(db.Integer, primary_key=True)
    message = db.Column(db.String)
    chat_id = db.Column(db.Integer, db.ForeignKey('chats.id'))
    chat = db.relationship('Chat', back_populates='messages', lazy='select')

    def __repr__(self):
        return f'Message ID {self.id}'

Next, let's set up the back-end API where the Socket.IO will do its job in both storing the message to the database and sending the message to all connected users. Here's a basic setup:

# app.py
from flask_socketio import emit
from flask import session
from config import app, db, api, socketio
from models import Message

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('message')
def handle_connect(data):
    try:
        message_text = data.get('message')
        new_message = Message(message=message_text)
        db.session.add(new_message)
        db.session.commit()
        emit("message_response", new_message.to_dict(), broadcast=True)
    except Exception as e:
        emit("chat_error", str(e), broadcast=False)

# other api routes here

if __name__ == '__main__':
    socketio.run(app)

Creating a React Component for Real-Time Chat:

First, you have to make sure you properly set up a React application using Vite. For questions on how to do this, refer to https://vitejs.dev/guide/. Once done, install the following dependency:

npm install socket.io-client

Now, let's create a React component that utilizes Socket.IO to enable real-time chat functionality. In the below example, I am incorporating socket into a React component that is also using formik and yup for form submission and validation:

import React, { useEffect, useState } from 'react';
import { useFormik } from "formik";
import * as yup from "yup";
import { io } from 'socket.io-client';

const ChatComponent = () => {
    const [messages, setMessages] = useState([]);
    const socket = io('http://localhost:5000'); // Replace url with yours

    useEffect(() => {
        socket.on('chat_result', (message) => {
            setMessages([...messages, message]);
        });

        socket.on('chat_error', (error) => {
            console.error('Chat error:', error);
        });

        return () => {
            socket.disconnect();
        };
    }, [messages]);

    const formSchema = yup.object().shape({
        message: yup.string().max(100)
    });

    const formik = useFormik({
        initialValues: {
            message: ''
        },
        validationSchema: formSchema,
        onSubmit: (values) => {
            socket.emit('message', { message: values.message });
            formik.resetForm();
        }
    });

    let renderedMessages = null
    if (messages[0]) {
        renderedMessages = messages.map((msg, i) => {
            return (
                <li key={i}>{msg}</li>
            )
        })
    }

    return (
        <form>
            <ul>{renderedMessages}</ul>
            <input
                type="text"
                value={formik.values.message}
                onChange={formik.handleChange}
            />
            <button onClick={formik.handleSubmit}>Send</button>
        </form>
    );
};

export default ChatComponent;

Conclusion:

Implementing Socket.IO in your React Flask project can greatly enhance the real-time communication capabilities, enabling features like live chat, notifications, and updates. For more information on Socket.IO, visit their documentation at https://socket.io/docs/v4/. Additionally, for more information about Flask-SocketIO, you can visit their documentation at https://flask-socketio.readthedocs.io/en/latest/. By following the steps outlined in this blog, you can easily integrate Socket.IO into your project and provide a more interactive user experience. Socket.IO's flexibility and ease of use make it a valuable tool for modern web applications.