Vue 3 is unable to detect any alterations made to an object that was created externally to the Vue component

I am working with a class called Character in a file named Character.ts

    /// This is called when server responds
    public setAttributeByType(type: StatsTypes, value: number): void {
        switch (type) {
            case StatsTypes.STRENGTH:
            case StatsTypes.DEXTERITY:
            case StatsTypes.VITALITY:
            case StatsTypes.INTELIGENCE:
                this.stats[type] = value;
                break;
            default: break;
        }
    }
    ....

The instance of the class is created outside of a Vue component in my "networking code":

    public static onStartGame(data:any):void {
        Player.character = new Character(data.data);
        Game.displayGamePage(PagesIndex.GAME_PAGE);
        requestAnimationFrame(Game.loop);
    }

This instance is then used in the main component:

Game.vue
import { defineComponent } from 'vue'
import Player from '@/Player/player';
import SceneManager, { Scenes } from '@/Render/scene_manager';
import Scene from '@/Render/scene';
import MainScene from "@/Render/scenes/main";
import MapScene from "@/Render/scenes/map";
import Game from '@/game/game';

// Components
import VplayerBar from "@/vue/subs/playerBar.vue";
import Vcharacter from "@/vue/subs/character.vue";

export enum GamePages {
    MAIN_PAGE = 1,
    MAP_PAGE,
}

export default defineComponent({
    name: "game",
    components: {
        VplayerBar,
        Vcharacter,
    },
    data() {
        return {
            page: GamePages.MAIN_PAGE,
            scenes: Scenes,
            gamePages: GamePages,
            player: Player,
            character: Player.character, /* <------------ Reference to class */
            pages: {
                character: false,
            }
        }
    },
})

...and passed down as a prop to the character.vue component

export default defineComponent({
    name: "character",
    props: {
        character: {  // <---- This prop does not change
            type: Character,
            required: true
        },
        toggleCharacter: {
            type: Function, 
            required: true
        }
    },
    components: {
        VBackpack,
        VInventory
    },
    data() {
        return {
            StatsTypes,
        }
    },
    methods: {
        toglePage() {
            this.toggleCharacter()
        },
        getPortrait(isMobile:boolean = false) {
            return Character.getCharacterPortrait(this.character.faction, this.character.gender, isMobile);
        },
        addPoint(attribute:StatsTypes, value: number) {
            if (GKeyHandler.keys[Keys.SHIFT_LEFT])
                value = 10;

            CharacterHandler.addAttributePoint(Player.character, attribute, value);
            //this.character.stats[StatsTypes.STRENGTH] += 1; 
        }
    }
});

The issue I am facing is that changes made to the character class instance outside the vue component (in my networking code) are not reflected in Vue. However, making changes directly inside the character.vue component works fine (as seen in the commented code in addPoint)

I attempted to use Proxy and Watch but it did not solve the problem.

Answer №1

Your issue seems to be related to an "identity" problem discussed here

In Vue 3, ES6 proxies are used to enable reactivity in objects. When you use const data = reactive(payload), the object referenced by data is actually different from the original payload. This is unlike Vue 2 where the object was directly modified with reactive setters/getters.

The same concept applies to the Options API that you are currently using. If you assign character: Player.character in the data() function, the resulting this.character (inside the Vue component) will be a distinct object from Player.character. You can verify this by comparing them like this:

console.log(this.character === Player.character)
within the mounted() method - the result will be false

Therefore, if you make changes using this.character (which is a Vue reactive proxy), Vue will detect these changes and trigger a re-render. However, if you modify the original object Player.character, Vue will not recognize this change...

A simple solution would be to utilize Vue's Composition API, which allows you to leverage Vue reactivity outside of Vue components.

import { reactive } from `vue`

Player.character = reactive(new Character(data.data));

By initializing data() in your Vue component with Player.character, Vue recognizes it as a reactive proxy already and does not wrap it in another proxy

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for resolving vertical alignment styling for images of varying sizes within Vue.js transition-group?

I have created an image slider example, but I'm facing a styling issue when using images of different dimensions. The element leaving the array is positioned absolutely for smooth transitions, but this causes the image to move up. I am trying to vert ...

Asynchronous jQuery operations using promises and finally functionality

I am attempting to interact with a REST api using jQuery's ajax feature. My goal is to return the Promise<Customer> object, but I am encountering an error stating that the property finally is missing. It used to work before, so I assume there h ...

Issue with React Context: The type 'Dispatch<SetStateAction<GiftsType>>' cannot be assigned to type '(arr1: string[], arr2: string[]) => void'

I'm currently working on a project in React+TS where I need to create a context that takes two string arrays and updates an object's state with these arrays. I keep encountering a title typo error in the setChoices function inside the return stat ...

Issue with displaying props data using v-model in VueJS

I am attempting to utilize a prop from the parent component as data in my child component. In the parent component: <ChangeCommentModal :comment="this.modalInfo.comment" /> And in the child component (ChangeCommentModal): props: ['co ...

Create a configuration featuring filter options similar to Notion's functionality

The objective is to create a system that can establish certain constraints, similar to the way Notion handles filter properties. https://i.sstatic.net/plctW.png System A sets up the constraints and System C evaluates them, both using Typescript. However, ...

Is there a way to pass a form error to the parent component as a parameter in React?

I am just starting to learn React. I have created a form and I want to inform the parent component about any input errors that occur. I attempted to use the variable myError as a prop similar to how I used the next method, but unfortunately, it did not wor ...

Ways to check if an array is empty in Typescript

Every time I attempt to log in, the first thing I do is validate the search data stored in the database by querying information from a matrix. This code can be found in login.controller.ts export async function postLogin(req: Request, res: Response): Pro ...

Angular 6 - Assigning string value to a variable when a click event occurs

I want to store the text of a click event in a variable. Here is the current code I am using: <th (click)="sortedColumn = clickValue">Ask Price</th> When a user clicks on the table header, I need it to save the value "Ask Price" in t ...

When defining a GraphQL Object type in NestJS, an error was encountered: "The schema must have unique type names, but there are multiple types named 'Address'."

Utilizing Nestjs and GraphQL for backend development, encountered an error when defining a model class (code first): Schema must contain uniquely named types but contains multiple types named "Address". Below is the Reader model file example: @ObjectType() ...

I'd like some clarification on the code that dynamically adds routes using Typescript and Node Express. Can someone please

Running my API server with node/typescript and express / express validator, I came across this code that I found really useful for separating route logic: function createCustomRouter(route: Array<CustomRouteEntry>): Router { const customRouter = R ...

After exiting the Vue PWA, a blank screen appears

After successfully creating a PWA Application, everything seems to be working fine. However, there is a problem: Users install the PWA on their desktop and it functions properly. But when they close and reopen it, only a blank page appears. The same i ...

Obtain information stored locally when an internet connection is established

I'm currently facing an issue with my Local Storage data retrieval process in Vuejs while being online. My ToDo app setup consists of Vuejs, Laravel, and MySQL. When the internet connection is available, I store data in localStorage. The ToDo app has ...

Angular is notifying that an unused expression was found where it was expecting an assignment or function call

Currently, I am working on creating a registration form in Angular. My goal is to verify if the User's username exists and then assign that value to the object if it is not null. loadData(data: User) { data.username && (this.registrationD ...

The Vuex store was initialized in main.js, however, it seems to be undefined when accessed in App

index.js: import Vue from "vue"; import Axios from "axios"; import App from "./App.vue"; import router from "./router"; import store from "./store"; const axios = Axios.create({ baseURL: process.env.VUE_APP_BASE_URL }) Vue.prototype.$http = axios; n ...

Setting the Vuetify drawer menu to be permanent on computer devices and non-permanent on mobile devices can be done by using conditional logic in your code

Can anyone assist me in making my drawer menu permanently displayed depending on the user's device? I was considering using the Vuetify grid system to determine whether I'm in LG, MD, or SM, but I'm not sure how to implement it. This is the ...

Send the data value to the header section

Is there a way to dynamically display the project number within the header component, which is visible on all pages? Here is the code for the Header component: <div class="d-flex flex-column flex-md-row align-items-center px-md-4 bg-brown text-wh ...

How should dynamic route pages be properly managed in NextJS?

Working on my first project using NextJS, I'm curious about the proper approach to managing dynamic routing. I've set up a http://localhost:3000/trips route that shows a page with a list of cards representing different "trips": https://i.stack. ...

A guide to testing the mui Modal onClose method

When using material UI (mui), the Modal component includes an onClose property, which triggers a callback when the component requests to be closed. This allows users to close the modal by clicking outside of its area. <Modal open={open} onCl ...

Dealing with Form Submission on Enter Keypress in VueJs

Transitioning from Angular to Vue was smooth until I encountered a frustrating issue that seems to have been ignored by VueJS developers. The challenge of allowing users to submit a form by pressing enter from an input field had me pulling my hair out. How ...

Choose the identical document within the Vuetify part v-file-input

I am currently working on a web application using nuxt, vuetify 2.x, and typescript. One issue I am facing is with uploading files using v-file-input. Specifically, if I select a file and then close the dialog without saving it, I am unable to select that ...