Search code examples
c#reactjsasp.net-coresignalr-hubreact-tsx

React .NET - Display all active connections with SignalR


I am working with a application using React as frontend and .net as backend.

Right now I am trying to add the connections as a number to show users how many people there is active on the application right now, I want the number to update in real-time, with this I am using SignalR.

What happends is when 1 user is active, instead of showing 1, it shows 3. When 2 users are active, it shows 6-7 and keeps going like this.

Let me show you some code

OnConnectionCountHub.cs

public class OnConnectionHub : Hub
{
    public static int ConnectionCount { get; set; }

    public override Task OnConnectedAsync()
    {
        ConnectionCount++;
        Clients.All.SendAsync("updateConnectionCount", ConnectionCount).GetAwaiter().GetResult();
        return base.OnConnectedAsync();
    }

    public override Task OnDisconnectedAsync(Exception? exception)
    {
        ConnectionCount--;
        Clients.All.SendAsync("updateConnectionCount", ConnectionCount).GetAwaiter().GetResult();
        return base.OnDisconnectedAsync(exception);
    }
}

Fairly simple.

ConnectionCount.tsx

export const ConnectionCount = () => {
    const [connectionCount, setConnectionCount] = useState(0)
    // create connection
    useEffect(() => {
        const connection = new HubConnectionBuilder()
        .withUrl(urlOnConnectionHub)
        .build()

    // connect to method that hub invokes
    connection.on("updateConnectionCount", (onConnection) => {
        setConnectionCount(onConnection)
        }
    )

    // start connection
    connection.start().then(() => {
        console.log("Connection started")
    });
    }, [])
    

    return(
        <section>
            <p>Active users: {connectionCount}</p>
        </section>
    )
}

My guess would be, because this is a component, it gets double the connections where ever I am using the component, instead of one connection all over.

Any idea on how to fix this? UseContext maybe?


Solution

  • The solution to this error was useContext

    The error I had, was the connection was made on every place I used the Component with signalR.

    This component did not work:

    export  const ConnectionCount = () => {
    const [connectionCount, setConnectionCount] = useState(0)
    
    const connection = new HubConnectionBuilder()
    .withUrl(urlOnConnectionHub)
    .build()
    
    connection.on("updateConnectionCount", (onConnection) => {
        setConnectionCount(onConnection)
     });
    
    useEffect(() => {        
          if (connection.state === HubConnectionState.Disconnected){
                connection.start().then(() => {
                    console.log("Connection started")
                });
            }                     
        }, []);
    
         return(
             <section>
                <p>Active users: {connectionCount}</p>
             </section>
      )
    }
    

    But I created a context:

    import React from 'react'
    
    const ConnectContext = React.createContext({
        connectionCount: 0
    });
    
    export default ConnectContext;
    

    I also created a context provider, where I took all of the logic from the signalR component:

    export const ConnectContextProvider = (props: ConnectContextProviderProps) => {
        const [connectionCount, setConnectionCount] = useState(0)
        
        const connection = new HubConnectionBuilder()
        .withUrl(urlOnConnectionHub)
        .build()
        
        connection.on("updateConnectionCount", (onConnection) => {
             setConnectionCount(onConnection)
         });
        
        useEffect(() => {        
              if (connection.state === HubConnectionState.Disconnected){
                    connection.start().then(() => {
                        console.log("Connection started")
                    });
                }                     
            // eslint-disable-next-line react-hooks/exhaustive-deps
            }, []);
    
            return <ConnectContext.Provider value={{connectionCount: connectionCount}}>{props.children}</ConnectContext.Provider>
    }
    
    interface ConnectContextProviderProps {
        children: ReactElement;
    };
    

    And in the previous signalR component, I wrote this:

    export const ConnectionCount = () => {
        
        const connectCtx = useContext(ConnectContext)
        return <p>Active Users - {connectCtx.connectionCount}</p>
    }
    

    and now I can use this component without getting a new connection everywhere Im using it.