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
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.
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?
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.