Saturday, 22 July 2023

In React, why is my array thought to be empty in a function triggered by a websocket message?

In React I have an array variable defined using useState, called messages. I also have a component on the page which doesn't have any DOM but rather listens for events on a websocket. When my component gets advised of an event from the websocket, the length of my messages variable is always zero - despite it having multiple messages previously. How come the array is empty at the time the function is entered?

Here's a minimal set of code which reproduces the issue:

WebSocketNotificationHandler.tsx:

import { useEffect } from "react";
import config from "../../../config.json";
import { IChatMessage } from "../../../services/chatService";

interface WebSocketNotificationHandlerProps {
    onReceiveMessages: (newMessages: IChatMessage[]) => void;
}

export let client: WebSocket = new WebSocket(config.webSocketAddress, config.webSocketProtocol);

const WebSocketNotificationHandler = (props: WebSocketNotificationHandlerProps) => {

    useEffect(() => {
        if (client) {
            client.onmessage = async (ev) => {
                handleWebSocketMessage(ev);
            }
        }
    }, []);

    const handleWebSocketMessage = async (ev: MessageEvent<string>) => {
        props.onReceiveMessages([]); // Simplified, normally we send one or more
    }

    return (
        <></>
    );

}

export default WebSocketNotificationHandler;

And GroupPage.tsx:

import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import chatService, { IChatMessage, ICreateMessageResponse } from '../../../services/chatService';
import ChatMessage from '../../atoms/ChatMessage/ChatMessage';
import ChatStagingArea from '../../organisms/ChatStagingArea/ChatStagingArea';
import WebSocketNotificationHandler from '../../organisms/WebSocketNotificationHandler/WebSocketNotificationHandler';

const GroupPage = () => {

    const { groupName } = useParams();

    const [messages, setMessages] = useState<IChatMessage[]>([]);

    const loadGroup = async () => {
        setMessages([]);
        await chatService // REST API to get current messages, returns a list >0 elements
            .getGroup(groupName)
            .then(data => {
                setMessages([...data.data.messages]);
            });
    }

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

    const handleMessageSent = (message: ICreateMessageResponse, text: string) => {
        let newMessages = [...messages, { /* Message data for a new message */ }];
        setMessages(newMessages);
    }

    const handleNewMessages = (newMessages: IChatMessage[]) => {
        console.log(messages.length); // Always 0 even if I can see multiple???
        // Code goes here that adds the newMessages to the messages array
    };

    return (
        <>
            {messages && messages.map(m => <ChatMessage key={m.id} author={m.fullName} date={m.authoredDate} message={m.message} />)}

            <ChatStagingArea groupName={groupName} onSent={(message, text) => handleMessageSent(message, text)} />

            <WebSocketNotificationHandler onReceiveMessages={newMessages => handleNewMessages(newMessages)} />
        </>
    );

}

export default GroupPage;


from In React, why is my array thought to be empty in a function triggered by a websocket message?

No comments:

Post a Comment