dmikester1
dmikester1

Reputation: 1382

React - state variable not reflecting changes

I am working on adding websockets to my Node/React app to automatically reflect changes to all the clients. So I have a websockets helper module that has onclose, onopen and onmessage events as well as a readyState function. So my component that needs the updated websocket values makes a call to that module and gets back data. That data variable is coming over empty, but when I console it out in the onmessage event in the module itself, it has all the info I want.

So here is how I call the websocket module in my component:

const onConnected = (socket) => {
    socket.send(
        JSON.stringify({
            eventType: 'clientCount'
        })
    );
};
    const { socket, readyState, reconnecting, data } = useWebsocket({
        url: wsURL + ':' + process.env.REACT_APP_WS_PORT,
        onConnected
    });

I have a useEffect that should spit out the updated values from data:

useEffect(() => {
    console.log('data changed!!!!');
    console.log({ data });
    console.log({ socket });
    console.log({ readyState });
    if (data) {
        setNumberClients(data.numberClients);
        setNumberIpads(data.numberIpads);
    }
}, [data, readyState]);

And finally here is my websockets module itself:

import { useState, useEffect, useRef } from 'react';

export default function useWebsocket({ url, onConnected }) {
    const [data, setData] = useState([]);
    const [reconnecting, setReconnecting] = useState(false);
    const socket = useRef(null);

    useEffect(() => {
        console.log('running socket hook');
        socket.current = new WebSocket(url);

        socket.current.onopen = () => {
            console.log('connected');
            onConnected(socket.current);
        };

        socket.current.onclose = () => {
            console.log('closed');
            if (socket.current) {
                if (reconnecting) return;
                setReconnecting(true);
                setTimeout(() => setReconnecting(false), 2000);
                socket.current.close();
                socket.current = undefined;
            }
        };

        socket.current.onmessage = (e) => {
            const wsData = JSON.parse(e.data);
            console.log('message received ', wsData);
            //setData((prev) => [...prev, wsData]);
            setData(wsData);
        };

        return () => {
            socket.current.close();
            socket.current = null;
        };
    }, [reconnecting, url]);

    const readyState = () => {
        if (socket.current) {
            switch (socket.current.readyState) {
                case 0:
                    return 'CONNECTING';
                case 1:
                    return 'OPEN';
                case 2:
                    return 'CLOSING';
                case 3:
                    return 'CLOSED';
                default:
                    return;
            }
        } else {
            return null;
        }
    };

    return {
        socket: socket.current,
        readyState: readyState(),
        reconnecting,
        data
    };
}

So data is always an empty array when I console it out in my component. But in the websockets module, it(wsData) has the info I need.

One More Thing: I am following the tutorial here: https://github.com/devmentorlive/websocket-direct-chat-client/tree/2-as-a-hook/src/chat

Update 2: I have a github repo showing the exact issue here: https://github.com/dmikester1/websockets-test Use Server and Start scripts to kick things off.

Upvotes: 1

Views: 1049

Answers (2)

Richard Zhan
Richard Zhan

Reputation: 500

It's not a problem on front-end side.

I think you have used useWebsocket hook twice - once in the SchedulePage and again in the ClientCountContainer so that you can check if 2 clients are displayed.

The problem is that the socket client you defined in ClientCountContainer component is not receiving the message from the server.

After looking at the websocket server, I noticed that it broadcasts messages to websocket clients that are saved in clients array. Not all the websocket client is saved in this array, but only the client which sends {eventType: 'connect'} message to the server is saved in that array.

The websocket client you created using useWebsocket hook in SchedulePage component is saved in clients array on websocket server, because it first sends {eventType: 'connect'} message to the server. But the websocket client you created in ClientCountContainer is not saved in that array.

Therefore, the messages containing clientCount information is not sent to the second websocket client which is defined in ClientCountContainer.

To get rid of this from happening, you can simply add this code snippet in the ClientCountContainer.js, which sends {eventType: 'connect} message to the server so that this client can be added to broadcast array.

    .....
    const onConnected = (socket) => {
        // Add these lines
        socket.send(
            JSON.stringify({
                eventType: 'connect'
            })
        );

        // This is the original line
        socket.send(
            JSON.stringify({
                eventType: 'clientCount'
            })
        );
    };
    ......

Please let me know if you have further issues. Thanks.

Upvotes: 1

medzz123
medzz123

Reputation: 229

I think that the useEffect would have to run all the time, not only when ur url changed or when it is reconnecting.

Try giving this a try: https://stackoverflow.com/a/60161181/10691892

Upvotes: 0

Related Questions