react-use-websocket icon indicating copy to clipboard operation
react-use-websocket copied to clipboard

useEffect throttling?

Open pkpk2 opened this issue 2 years ago • 8 comments
trafficstars

I run into a situation when useEffect invocation was skipped during a higher number of incoming events

two scenarios to compare:

  1. websocket receives 30 events with a delay 1 second between them. useEffect/lastMessage combination was able to process all of them.

  2. websocket receives 30 events without the delay, useEffect/lastMessage have been invoked only for a small subset of the events, so most of the events had been skipped essentially from the main execution path.

In both cases onMessage handler showed all expected events had been received.

Any thoughts how this situation can be handled without doing a custom onMessage and buffering? Should the framework do the proper buffering and return a list of events since the last access vs. just the last one?

pkpk2 avatar Jul 04 '23 09:07 pkpk2

i've got the same problem too, did you manage to create a workaround?

msmaiaa avatar Aug 03 '23 00:08 msmaiaa

i've got the same problem too, did you manage to create a workaround?

@msmaiaa Yes, i created a workaround:

    1. Added a buffer for incoming events with useRef to avoid re render on each event
    1. Added direct onMessage callback to useWebsocket to update the useRef buffer from step 1 with the incoming events
    1. From the onMessage callback you have to have a condition to flush the events regularly or at the end and trigger re-render, this will depend on your app logic: every N events, by timer, etc.

pkpk2 avatar Aug 03 '23 00:08 pkpk2

@pkpk2 Does your solution work with the shared option? I've been brainstorming a couple of ideas for handling this situation, but have not come up with one that works efficiently where there are multiple subscribers.

robtaussig avatar Sep 05 '23 02:09 robtaussig

@pkpk2 Hi, what do you mean by useRef buffer? Could you elaborate more?

melihcoban avatar Sep 06 '23 11:09 melihcoban

@melihcoban he likely collects the messages in a useRef array and then retrieves them (and empties the array) in a throttled/debounced manner.

robtaussig avatar Sep 06 '23 12:09 robtaussig

@robtaussig I see, I kind of have the same issue with the original post, however like you said using shared in the websocket complicates things.

melihcoban avatar Sep 06 '23 12:09 melihcoban

@pkpk2 Does your solution work with the shared option? I've been brainstorming a couple of ideas for handling this situation, but have not come up with one that works efficiently where there are multiple subscribers.

@robtaussig I have not tried shared unfortunately. Can not confirm, but i can try in a few days.

@melihcoban he likely collects the messages in a useRef array and then retrieves them (and empties the array) in a throttled/debounced manner.

Correct. useRef is used to do the data buffering without triggering the components re rendering on each event.

pkpk2 avatar Sep 08 '23 08:09 pkpk2

My implementation as an example - I dequeue 1 message each time so as not to flood the state updates as I can get many messages in short bursts.

    // Create a ref array so we don't trigger rendering on mutation
    const messageList = useRef<TMessage[]>([]);

    // Put messages into a buffer for later processing
    const onMessageCallback = (event: MessageEvent<string>) => {
        if (event.data !== 'undefined') {
            const message: TMessage = JSON.parse(event.data);
            messageList.current.push(message);
        }
    };

    useWebSocket(`${EVENTS_ROOT}/session/${id}`, {
        onMessage: onMessageCallback,
    });

    useEffect(() => {
        const t = setInterval(() => {
            const updateMessage = messageList.current.shift();
            if (updateMessage !== undefined) {
               // Do something with your message here
                }
            }
        }, 200);

        // Clean up
        return () => {
            clearInterval(t);
        };
    }, [id]);

robinelvin avatar Jan 23 '24 13:01 robinelvin