DEV Community

tom-takeru
tom-takeru

Posted on

Real-Time Web Application demo with WebSocket - Frontend

Introduction

In this article, I will explore the frontend implementation of our real-time WebSocket application. Built with Next.js and TypeScript, the frontend serves as an interactive interface for sending and receiving real-time messages. Let's dive into the details of how this application is structured and how the components interact to provide a seamless WebSocket experience.


Project Structure

https://github.com/tom-takeru/web-socket-demo

The frontend project is organized to ensure modularity and reusability. Below is the updated directory structure:

./frontend/src
├── app
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── components
│   ├── HomePage.tsx
│   ├── message
│   │   ├── MessageInputForm.tsx
│   │   ├── MessageList.tsx
│   │   └── MessageSection.tsx
│   └── websocket
│       ├── WebSocketControls.tsx
│       ├── WebSocketSection.tsx
│       └── WebSocketStatus.tsx
└── hooks
    └── useMessages.ts
Enter fullscreen mode Exit fullscreen mode

Key Directories and Files

  • app/: Contains global styles and layout definitions for the application.
  • components/: Includes the primary components for messages and WebSocket handling.
  • hooks/: Contains custom hooks, such as useMessages, for managing state and logic.
  • HomePage.tsx: The central page that ties everything together.

Core Component: HomePage.tsx

HomePage.tsx is the main entry point for the WebSocket application. It integrates message and WebSocket-related components while managing the application's state and lifecycle.

Code Walkthrough

"use client";

import { useState, useRef, useEffect } from "react";
import { useMessages } from "@/hooks/useMessages";
import MessageSection from "@/components/message/MessageSection";
import WebSocketSection from "@/components/websocket/WebSocketSection";

export default function HomePage() {
  const { messages, clearMessages, addMessage } = useMessages();
  const [connectionStatus, setConnectionStatus] = useState<
    "Connected" | "Disconnected"
  >("Disconnected");
  const [isSending, setIsSending] = useState<boolean>(false);
  const [message, setMessage] = useState<string>("");
  const wsRef = useRef<WebSocket | null>(null);

  useEffect(() => {
    return () => {
      if (wsRef.current) {
        wsRef.current.close();
      }
    };
  }, []);

  const handleOpen = () => {
    console.log("Connected to WebSocket");
    setConnectionStatus("Connected");
  };

  const handleMessage = (event: MessageEvent) => {
    console.log("Message from server:", event.data);
    const data = JSON.parse(event.data);
    addMessage(data.message, data.timestamp);
    setMessage("");
    setIsSending(false);
  };

  const handleError = (error: Event) => {
    console.log("WebSocket error:", error);
    alert("Failed to connect to WebSocket.");
  };

  const handleClose = () => {
    console.log("WebSocket closed");
    setConnectionStatus("Disconnected");
    wsRef.current = null;
  };

  const startWebSocket = () => {
    if (wsRef.current && wsRef.current.readyState !== WebSocket.CLOSED) {
      alert("WebSocket is already open");
      return;
    }

    clearMessages();

    const ws = new WebSocket("ws://localhost:8080/ws");
    wsRef.current = ws;

    ws.onopen = handleOpen;
    ws.onmessage = handleMessage;
    ws.onerror = handleError;
    ws.onclose = handleClose;
  };

  const stopWebSocket = () => {
    if (!wsRef.current) {
      alert("WebSocket is not open");
      return;
    }
    wsRef.current.close();
    wsRef.current = null;
    setConnectionStatus("Disconnected");
  };

  const sendMessage = async () => {
    if (message.trim() === "") {
      alert("Message cannot be empty");
      return;
    }

    if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
      alert("WebSocket is not open");
      return;
    }

    setIsSending(true);
    const jsonMessage = JSON.stringify({ message });
    wsRef.current.send(jsonMessage);
    setMessage("");
  };

  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-8 pb-20 sm:p-20 font-sans">
      <h1 className="text-3xl font-bold">WebSocket Demo App</h1>
      <main className="flex flex-col gap-2 items-center w-full max-w-md">
        <MessageSection
          messages={messages}
          message={message}
          setMessage={setMessage}
          sendMessage={sendMessage}
          isDisabled={connectionStatus === "Disconnected" || isSending}
        />
        <WebSocketSection
          connectionStatus={connectionStatus}
          startWebSocket={startWebSocket}
          stopWebSocket={stopWebSocket}
        />
      </main>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Key Functionalities

  1. State Management: Manages connection status, current message, and sending state.
  2. WebSocket Lifecycle: Handles connection setup, events (onopen, onmessage, onerror, onclose), and teardown.
  3. User Interaction: Provides clear feedback and controls for starting/stopping the connection and sending messages.

Conclusion

The HomePage.tsx component demonstrates the integration of WebSocket functionalities with a clean React structure. Its focus on state management and user interaction makes it the backbone of the application's frontend.

In the next article, I will explore the backend implementation using Gin and Go, detailing how the WebSocket server handles connections and messages.


Links to the Series

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay