Notes

.NET Core SignalR with React Native

Edit on GitHub


ReactJS
3 minutes
1# .NET Core 2.2
2#npm i @aspnet/signalr
3#yarn add @aspnet/signalr
4
5# .NET Core 3.0
6npm install @microsoft/signalr

@microsoft/signalr works with .NET Core 3.1 and has a withAutomaticReconnect() function that we can use for reconnecting if connection lost. This makes code simpler. And it works with WebSocket by default so we don’t need the heartbeat either.

1const connection = new SignalR.HubConnectionBuilder()
2  .withUrl('https://api.mysite.com/signalhub', {
3    accessTokenFactory: () => TOKEN
4  })
5  .configureLogging(SignalR.LogLevel.Debug)
6  .withAutomaticReconnect() // this will reconnect if connection lost
7  .build()

Working code example (@aspnet/signalr)

 1// App.tsx
 2import React, { useEffect, useState } from 'react';
 3import { StyleSheet, Text, View } from 'react-native';
 4import * as SignalR from '@aspnet/signalr'
 5
 6
 7export default function App() {
 8  const [connectionState, setConnectionState] = useState(false)
 9  const [connectionError, setConnectionError] = useState()
10  useEffect(() => {
11    signalr()
12  }, []) // passing an empty array makes sure this only runs on mount/unmount and not everytime props/state changes. if you give it something inside the array it'll only update when that something updates
13
14  function signalr() {
15    // das function to start a connection with SignalR hub for our app
16    const token = "mySuperSecretToken"
17    const connection = new SignalR.HubConnectionBuilder()
18      .withUrl('https://api.mysite.com/signalhub', {
19        accessTokenFactory: () => token
20      })
21      .configureLogging(SignalR.LogLevel.Debug)
22      .build()
23  
24    async function start() {
25      try {
26        await connection.start()
27        setConnectionState(true)
28        
29        setInterval(() => {
30          connection.invoke('heartbeat')
31          console.log(`beat`)
32        }, 15000) // Send heartbeat every 15 secs.
33        setConnectionError('')
34        console.log('CONNECT ho gya!')
35      } catch (error) {
36        console.error(`Disconnected with status code ${error.statusCode}`, JSON.stringify(error))
37        setConnectionError(JSON.stringify(error))
38        setTimeout(() => start(), 5000) // Retry connection after 5secs 
39      }
40    }
41  
42    // connection.serverTimeoutInMilliseconds = 3600000
43    // connection.keepAliveIntervalInMilliseconds = 10000
44    start()
45  
46    // Reconnect
47    connection.onclose(async (error) => {
48      setConnectionState(false)
49      setConnectionError(JSON.stringify(error))
50      console.log('awwww =( chala gya')
51      console.error(`Disconnected with status code ${error.statusCode}`, JSON.stringify(error))
52      await start()
53    })
54    
55    // Listen for events
56    connection.on('messageReceived', (data) => {
57      console.log(`\n data: \n ${JSON.stringify(data, null, '\t')} \n`)
58    })
59
60    connection.on('notificationReceived', (data) => {
61      console.log(`\n data: \n ${JSON.stringify(data, null, '\t')} \n`)
62    })
63  }
64
65  
66  return (
67    <View style={styles.container}>
68      <Text>
69        You are <Text style={styles.boldText}>{connectionState ? <Text style={styles.greenText}>connected</Text> : <Text style={styles.redText}>disconnected</Text>}</Text>
70        <Text>{connectionError ? connectionError : null }</Text>
71      </Text>
72    </View>
73  );
74}
75
76const styles = StyleSheet.create({
77  container: {
78    flex: 1,
79    backgroundColor: '#fff',
80    alignItems: 'center',
81    justifyContent: 'center',
82  },
83  boldText: {
84    fontWeight: 'bold'
85  },
86  greenText: {
87    color: '#2ECC71',
88  },
89  redText: {
90    color: '#E74C3C'
91  }
92});

Notes

  • You need to invoke something at regular intervals in order to stay connnected. Otherwise it’ll timeout the connection every 30/60 secs..
  • Doesn’t matter if the method you’re invoking exists. If it doesn’t, you’ll get a HubException error about method not existing, but you’ll stay connected nonetheless.
  • i have tried setting the keepAliveIntervalInMilliseconds and serverTimeoutInMilliseconds values on the frontend but that didn’t work
  • The { accessTokenFactory: () => token } bit alllows you to pass a token for authorization.
  • There are different LogLevels. Debug will give you more details