1

I have a Vue 3.x SFC and a custom typescript class Client as seen below.

The Client class starts a websocket connection with another server of mine and is going to receive messages containing a user count string and other information.

When I use this.client.userCount = "something" from mounted() the page reacts as expected with the span having the new string.

When the Websocket receives the user message with a new string, client.userCount is updated accordingly, but the page does not re-render with the new information.

What do I need adjust for changes made from within the class to be reflected by the reactivity system the same way changes made from outside the class are?

<script setup>
  import { Client } from '@/path/to/client';
</script>

<template>
  <span>{{ users }}</span> online
</template>

<script>
  export default {
    data() {
      return {
        client: new Client(),
      }
    },
    computed: {
      users() {
        return this.client.userCount;
      },
    },
  }
</script>

Client that is being imported from Client.ts:

export class Client {

  // User counter
  public userCount: string = "?";
  public connected: boolean = false;

  constructor() {
        this.websocket = new WebSocket("MYWEBSITE");

        this.websocket.onopen = () => {
            console.log("client connection open");
            this.connected = true;
        }

        // Process received websocket data
        this.websocket.onmessage = (event: any) => {
            try {
                let data = JSON.parse(event.data);
                console.log('message received', data);
                switch (data.type) {
                    case 'users':
                        this.userCount = data.count.toString();
                        break;
                    default:
                        console.error("unsupported event", data);
                }
            } catch (err) {
                console.log("Error occurred processing message:", err);
                console.log(event);
            }
        };

        this.websocket.onclose = () => {
            this.connected = false;
            console.log('client connection closed');
        }
    }
}
  • Please, provide https://stackoverflow.com/help/mcve for your problem. A simplified example won't work. It's unknown what exactly happens inside a class, there is supposed to be an error somewhere – Estus Flask Oct 09 '22 at 23:14
  • @EstusFlask Good point, I apologize I am not familiar with that aspect of Stack Overflow Yet and will look into it. I will be updating the question shortly with new info as well as I have narrowed down the problem. – sam whittle Oct 10 '22 at 00:20
  • Basically don't create callbacks that use `this` inside class constructor, they capture non-reactive class instance – Estus Flask Oct 10 '22 at 05:28
  • @EstusFlask Thank you! Looks like I wasn't able to find those questions in my googling. I will try implementing an init() function and moving the callbacks into it. I tried someone else's idea of adding reactive from vue to the class itself and that also resolved the issue, but I like your method better as it allows the class to remain unchained to vue to an extent. – sam whittle Oct 10 '22 at 13:20
  • Vue doesn't play too well in classes because of the necessity of `init`. But the use of reactive api inside classes has pitfalls notably refs and computeds, I'd recommend against doing this – Estus Flask Oct 10 '22 at 21:30

0 Answers0