3

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?

rene
  • 41,474
  • 78
  • 114
  • 152
Joro
  • 311
  • 4
  • 15

2 Answers2

1

try this on client side :

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>
  )
}
Mohammad Aghazadeh
  • 2,108
  • 3
  • 9
  • 20
  • Thanks for your answer, sadly it did not work. – Joro Sep 18 '22 at 09:32
  • I tested it and there is no problem, what happens now? – Mohammad Aghazadeh Sep 18 '22 at 09:43
  • You are right, this does work now. However, in order for it to work I could not use it as a reuseable component, but I needed to add it directly into the main page of the site I wanted to use it on. But it works anyway, thanks friend! – Joro Sep 18 '22 at 13:10
  • After I tried some time, sadly it still works as before. When ever want to create another connection on another component, it doubles instead of just adding 1 new connection – Joro Sep 18 '22 at 14:10
0

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.

Joro
  • 311
  • 4
  • 15