Next.js Discord

Discord Forum

how to prevent nextjs component from re rendering whenever a new message is received through wsocke?

Answered
Pacific sandlance posted this in #help-forum
Open in Discord
Pacific sandlanceOP
hey guys, I've noticed that if I connect to my websocket and receive messages, it rerenders the component and re establishes the component, I've already added code logic to not connect a second time but is there an other way to prevent the component from re-rendering?
Answered by Pacific sandlance
Ah I see what is happening, looked further into it and if I disable react strict mode everything works perfectly
View full answer

24 Replies

@Pacific sandlance Where can I can find more info in docs?
you don't need docs for it
just connect to your websocket in parent component and pass it as prop to child
Pacific sandlanceOP
oh I see
Initiate the connection in index.js for example and emit the received data to <Child props={props} /> ?
@@ts-ignore just connect to your websocket in parent component and pass it as prop to child
Pacific sandlanceOP
still connects 2 times smh
scratched the reusable hook completely, I will just use a simpler one in every class because I cba at this point fixing it
Asian black bear
How are you connecting to a we socket that causes any re-render at all?
Are you keeping something in state that belongs in a ref?
import { useEffect, useRef } from 'react';
import EventEmitter from 'events'; // You can also use Node's native events module

const useSocketListener = (socketUrl, callback) => {
  const eventEmitterRef = useRef(new EventEmitter());
  const cbCalledRef = useRef(false);

  useEffect(() => {
    const listen = () => {
      let lastReceived = Date.now();
      const socket = new WebSocket(socketUrl);
      let resetId = setInterval(() => {
        if (Date.now() > lastReceived + 10 * 1000) {
          try {
            socket.ping();
          } catch (err) {}
        }

        if (Date.now() - lastReceived > 20 * 1000) {
          try {
            socket.terminate();
          } catch (err) {
            clearInterval(resetId);
            listen();
          }
        }
      }, 15000);

      socket.addEventListener('open', () => {
        console.log(`Connected to ${socketUrl}`);
        lastReceived = Date.now();
        if (!cbCalledRef.current) {
          cbCalledRef.current = true;
          callback(eventEmitterRef.current);
        }
      });

      socket.addEventListener('close', () => {
        clearInterval(resetId);
        listen();
      });

      socket.addEventListener('pong', () => {
        lastReceived = Date.now();
      });

      socket.addEventListener('ping', () => {
        lastReceived = Date.now();
        try {
          socket.pong();
        } catch (error) {
          console.log(error);
        }
      });

      socket.addEventListener('message', (event) => {
        eventEmitterRef.current.emit('message', event.data);
        lastReceived = Date.now();
      });
    };

    listen();

    return () => {
      // Cleanup logic if needed
    };
  }, [socketUrl, callback]);

  return eventEmitterRef.current;
};

export default useSocketListener;
Asked chat gpt to convert a function I had from plain js to a hook
example usage:
import React from 'react';

const MyComponent = () => {
  const handleEvent = (eventEmitter) => {
    eventEmitter.on('message', (message) => {
      console.log('Received message:', message);
    });
  };

  useSocketListener('ws://your-websocket-url', handleEvent);

  return (
    <div>
      {/* Your component logic */}
    </div>
  );
};
Asian black bear
It strikes me that, being in useEffect, all of your setup code is guaranteed to be called twice
because that is just how useEffect works
@Pacific sandlance js import { useEffect, useRef } from 'react'; import EventEmitter from 'events'; // You can also use Node's native events module const useSocketListener = (socketUrl, callback) => { const eventEmitterRef = useRef(new EventEmitter()); const cbCalledRef = useRef(false); useEffect(() => { const listen = () => { let lastReceived = Date.now(); const socket = new WebSocket(socketUrl); let resetId = setInterval(() => { if (Date.now() > lastReceived + 10 * 1000) { try { socket.ping(); } catch (err) {} } if (Date.now() - lastReceived > 20 * 1000) { try { socket.terminate(); } catch (err) { clearInterval(resetId); listen(); } } }, 15000); socket.addEventListener('open', () => { console.log(`Connected to ${socketUrl}`); lastReceived = Date.now(); if (!cbCalledRef.current) { cbCalledRef.current = true; callback(eventEmitterRef.current); } }); socket.addEventListener('close', () => { clearInterval(resetId); listen(); }); socket.addEventListener('pong', () => { lastReceived = Date.now(); }); socket.addEventListener('ping', () => { lastReceived = Date.now(); try { socket.pong(); } catch (error) { console.log(error); } }); socket.addEventListener('message', (event) => { eventEmitterRef.current.emit('message', event.data); lastReceived = Date.now(); }); }; listen(); return () => { // Cleanup logic if needed }; }, [socketUrl, callback]); return eventEmitterRef.current; }; export default useSocketListener; Asked chat gpt to convert a function I had from plain js to a hook
useEffect runs twice
Pacific sandlanceOP
I thought it run once 💀
Asian black bear
yeah it is really annoying
Pacific sandlanceOP
alright lemme check
Asian black bear
A common paradigm is to use a boolean ref to simply ignore the first run
Pacific sandlanceOP
Ah I see what is happening, looked further into it and if I disable react strict mode everything works perfectly
Answer
Pacific sandlanceOP
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false, //previously true
}

module.exports = nextConfig
Thanks @Asian black bear @@ts-ignore
Pacific sandlanceOP
fyi code still needs to be finished but should be a good base