Why does React StrictMode create duplicate WebSocket subscriptions even when cleanup runs correctly?
18:11 13 Jun 2026

I'm debugging a React 18 application that establishes a WebSocket connection inside a custom hook.

function useSocket(url) {
  useEffect(() => {
    const socket = new WebSocket(url);

    socket.onopen = () => {
      console.log("Connected");
    };

    return () => {
      socket.close();
      console.log("Closed");
    };
  }, [url]);
}

The component using this hook is wrapped in .

In development mode I see:

Connected
Closed
Connected

This causes issues because my backend briefly registers two connections and triggers duplicate subscription events.

I understand that React intentionally invokes effects twice in development, but I'm trying to understand the recommended pattern for WebSocket connections, SSE streams, or third-party SDK initialization.

Questions:

  1. Is React's double invocation guaranteed to always call cleanup before the second mount?

  2. Should external resources like WebSockets be initialized outside React effects?

  3. How do production-grade applications prevent duplicate side effects while still keeping StrictMode enabled?

  4. Is there an official React-recommended pattern for long-lived connections?

I'm looking for guidance beyond simply disabling StrictMode.

javascript reactjs websocket