Skip to main content

Properly closing SignalR connections is crucial for resource management, avoiding memory leaks, reducing server load, ensuring network efficiency, enhancing security, and providing a good user experience. By implementing proper connection management in your Vue.js 3 application, you can ensure that resources are used efficiently and the application remains performant and reliable.

---
title: How to properly close SignalR connection in Vue.js
subtitle: Properly closing SignalR connections is crucial for resource management, avoiding memory leaks, reducing server load, ensuring network efficiency, enhancing security, and providing a good user experience. By implementing proper connection management in your Vue.js 3 application, you can ensure that resources are used efficiently and the application remains performant and reliable.
author: Jon LaBelle
date: September 29, 2024
source: https://jonlabelle.com/snippets/view/markdown/how-to-properly-close-signalr-connection-in-vuejs
notoc: false
---

## Why you need to ensure SignalR connections are properly closed

Ensuring that SignalR connections are properly closed is important for several reasons:

### 1. **Resource Management**

- **Reason**: SignalR connections consume server and client resources, such as memory and network bandwidth.
- **Impact**: Leaving connections open unnecessarily can lead to resource exhaustion, which can degrade the performance of your application and server.

### 2. **Avoiding Memory Leaks**

- **Reason**: Unclosed connections can lead to memory leaks, as resources allocated for the connection are not released.
- **Impact**: Over time, this can cause the application to consume more memory than necessary, potentially leading to crashes or slow performance.

### 3. **Server Load**

- **Reason**: Each open connection adds load to the server.
- **Impact**: Keeping unnecessary connections open can increase server load, reducing the server's ability to handle new connections and requests efficiently.

### 4. **Network Efficiency**

- **Reason**: Open connections continuously consume network bandwidth.
- **Impact**: This can lead to increased latency and reduced network performance for other applications and services.

### 5. **Security**

- **Reason**: Open connections can be a potential security risk if not properly managed.
- **Impact**: Ensuring connections are closed when no longer needed reduces the attack surface and potential vulnerabilities.

### 6. **User Experience**

- **Reason**: Properly managing connections ensures that users have a responsive and reliable experience.
- **Impact**: Users may experience delays or errors if connections are not managed properly, leading to a poor user experience.

## How to handle closing SignalR connections

To handle closing SignalR connections in your Vue.js 3 application using the Composition API, you need to ensure that the connection is properly stopped in various scenarios. Here are some common use cases where you need to close SignalR connections, along with examples for each use case:

### Use Cases for Closing SignalR Connections

1. **Component Unmounting**
2. **User Logout**
3. **Page Navigation**
4. **Application Shutdown**

### Step-by-Step Plan

1. **Create SignalR Connection Composable:**

   Set up the SignalR connection using a composable function.

2. **Handle Connection Closure in Different Use Cases:**

   Implement logic to stop the connection in various scenarios.

### Code Implementation

#### 1. Create SignalR Connection Composable

By implementing a singleton pattern for the SignalR connection, you can ensure that only one instance of the connection is created and reused throughout your Vue.js 3 application. This prevents multiple connections from being opened and ensures efficient resource usage and consistent connection management.

Create a file `src/composables/useSignalR.js` to manage the SignalR connection using the Composition API.

```javascript
// src/composables/useSignalR.js
import { ref, onUnmounted } from 'vue';
import * as signalR from '@microsoft/signalr';

let instance = null;

export function useSignalR(url) {
  if (instance) {
    return instance;
  }

  const connectionState = ref('disconnected');
  const connection = new signalR.HubConnectionBuilder()
    .withUrl(url, {
      withCredentials: true // Ensure cookies are sent with requests
    })
    .configureLogging(signalR.LogLevel.Information)
    .build();

  async function startConnection(retries = 5, delay = 1000) {
    for (let attempt = 1; attempt <= retries; attempt++) {
      try {
        await connection.start();
        connectionState.value = 'connected';
        console.log('SignalR connected');
        return;
      } catch (err) {
        console.error(`SignalR connection attempt ${attempt} failed: `, err);
        if (attempt === retries) {
          console.error('All connection attempts failed');
        } else {
          const backoffDelay = delay * Math.pow(2, attempt - 1);
          console.log(`Retrying in ${backoffDelay} ms...`);
          await new Promise((resolve) => setTimeout(resolve, backoffDelay));
        }
      }
    }
  }

  async function stopConnection() {
    try {
      await connection.stop();
      connectionState.value = 'disconnected';
      console.log('SignalR connection stopped');
    } catch (err) {
      console.error('Error stopping SignalR connection: ', err);
    }
  }

  connection.onclose(async (error) => {
    connectionState.value = 'disconnected';
    if (error) {
      console.error('SignalR connection closed with error: ', error);
    } else {
      console.log('SignalR connection closed.');
    }
    // Attempt to reconnect
    setTimeout(() => startConnection(), 5000);
  });

  connection.onreconnecting((error) => {
    connectionState.value = 'reconnecting';
    console.warn('SignalR reconnecting due to error: ', error);
  });

  connection.onreconnected((connectionId) => {
    connectionState.value = 'connected';
    console.log('SignalR reconnected. Connection ID: ', connectionId);
  });

  onUnmounted(async () => {
    await stopConnection()
    console.log('SignalR connection stopped')
  })

  instance = {
    connectionState,
    startConnection,
    stopConnection,
    connection
  };

  return instance;
}
```

#### 2. Use the Composable in Different Use Cases

##### 2.1 Component Unmounting

In your Vue.js component, use the `useSignalR` composable and ensure the connection is stopped when the component is destroyed.

```javascript
<!-- src/components/YourComponent.vue -->
<template>
  <div>
    <p>Connection State: {{ connectionState }}</p>
    <button @click="sendMessage('Hello, SignalR!')">Send Message</button>
  </div>
</template>

<script>
import { onMounted, onBeforeUnmount } from 'vue';
import { useSignalR } from '../composables/useSignalR';

export default {
  setup() {
    const { connectionState, startConnection, stopConnection, connection } = useSignalR('https://your-api-url/yourHub');

    onMounted(() => {
      startConnection();
    });

    onBeforeUnmount(() => {
      stopConnection();
    });

    function sendMessage(message) {
      connection.invoke("SendMessage", message)
        .catch(err => console.error("Error sending message: ", err));
    }

    return {
      connectionState,
      sendMessage
    };
  }
};
</script>
```

##### 2.2 User Logout

In your authentication service or logout method, stop the SignalR connection when the user logs out.

```javascript
// src/services/authService.js
import { useSignalR } from '../composables/useSignalR';

const { stopConnection } = useSignalR('https://your-api-url/yourHub');

export function logout() {
  // Perform logout operations
  // ...

  // Stop the SignalR connection
  stopConnection();
}
```

### Summary

By using the Composition API and the `useSignalR` composable, you can manage the SignalR connection efficiently in your Vue.js 3 application. The connection is started when needed and stopped in various scenarios such as component unmounting, user logout, page navigation, and application shutdown. This ensures that resources are properly released and the connection is managed efficiently.