Unexpected behavior found in Vue 3 when using Proxy for reactivity in classes

I am facing an issue with a Class that has a proxy-based object. The set() method modifies another property of the same class, and everything runs smoothly when the code is executed in JS/TS.

class Form {
  errors = []
  values = {}

  constructor(values) {
    this.values = new Proxy(values, {
      set: (target, prop, value) => {
        target[prop] = value

        this.errors.push('test')

        return true
      },
    })
  }
}

const form = new Form({
  name: 'Jhon',
  age: 20,
})

form.values.name = 'Maria'
form.values.age = 30

console.log(form.errors)

My goal is to have the expected result for form.errors, which should be an Array like ['test', 'test']

However, when I use Vue and include {{ form.errors }} in the <template>, it does not react as expected. It doesn't update in real-time.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@3"></script>
  </head>
  <body>
    <script src="https://unpkg.com/vue@3"></script>

    <div id="app">{{ form.errors }}</div>

    <script>
      class Form {
        errors = []
        values = {}

        constructor(values) {
          this.values = new Proxy(values, {
            set: (target, prop, value) => {
              target[prop] = value

              this.errors.push('test')

              return true
            },
          })
        }
      }

      const app = Vue.createApp({
        data() {
          return {
            form: new Form({
              name: 'Jhon',
              age: 20,
            }),
          }
        },
        mounted() {
          this.form.values.name = 'Maria'
          this.form.values.age = 30
        },
      })

      app.mount('#app')
    </script>
  </body>
</html>

form.errors gets updated, but Vue fails to observe these changes accurately. This can be verified by adding:

mounted() {
  this.form.values.name = 'Maria'
  this.form.values.age = 30

  this.form.errors.push('hello')
}

This will give the expected output on the DOM:

['test', 'test', 'hello']

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@3"></script>
  </head>
  <body>
    <script src="https://unpkg.com/vue@3"></script>

    <div id="app">{{ form.errors }}</div>

    <script>
      class Form {
        errors = []
        values = {}

        constructor(values) {
          this.values = new Proxy(values, {
            set: (target, prop, value) => {
              target[prop] = value

              this.errors.push('test')

              return true
            },
          })
        }
      }

      const app = Vue.createApp({
        data() {
          return {
            form: new Form({
              name: 'Jhon',
              age: 20,
            }),
          }
        },
        mounted() {
          this.form.values.name = 'Maria'
          this.form.values.age = 30

          this.form.errors.push('okay')
        },
      })

      app.mount('#app')
    </script>
  </body>
</html>

I am looking for a solution where form.errors becomes reactive in Vue just like any other property.

Answer №1

To ensure that Form#errors remains reactive in this scenario, make sure to initialize it using Vue.reactive():

class Form {
  errors = Vue.reactive([])
  ⋮
}

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@3"></script>
  </head>
  <body>
    <div id="app">form.errors: {{ form.errors }}</div>

    <script>
      class Form {
        errors = Vue.reactive([])
        values = {}

        constructor(values) {
          this.values = new Proxy(values, {
            set: (target, prop, value) => {
              target[prop] = value

              this.errors.push('test')

              return true
            },
          })
        }
      }

      const app = Vue.createApp({
        data() {
          return {
            form: new Form({
              name: 'Jhon',
              age: 20,
            }),
          }
        },
        mounted() {
          this.form.values.name = 'Maria'
          this.form.values.age = 30
          
          this.form.errors.push('hello')
        },
      })

      app.mount('#app')
    </script>
  </body>
</html>

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

I have successfully converted an SQL Join query into JSON, but now I am unsure of how to interact with the

I recently ran an SQL Join on two tables and obtained the following results: _____People_____ name: "Jane" age: 35 job_id: 1 _____Professions_____ job_id: 1 title: "Teacher" "SELECT * FROM People INNER JOIN Professions ON People.job_id = Professions.job ...

The Angular application will only display updated dynamic content upon refreshing the page

I am utilizing a factory to access my server-side data, using $routeParams to pass the id in the server request. Initially, everything functions correctly, but issues arise after the first run through the program. Here is a snippet of my code: Controller: ...

The challenge with the Optional Chaining operator in Typescript 3.7@beta

When attempting to utilize the Typescript optional chaining operator, I encountered the following exception: index.ts:6:1 - error TS2779: The left-hand side of an assignment expression may not be an optional property access. Here is my sample code: const ...

Tips on showing binary information as images using extjs 4

As the proud owner of a valid .JPEG image's binary data, I am on a quest to convert this information into an actual viewable image using Python. Seeking advice on how to successfully transform this binary code into a visually appealing .JPEG format w ...

Adjust the slide count accordingly when navigating to a particular item within the Bootstrap 3 Carousel

I made adjustments to the standard Bootstrap 3 Carousel so that it can navigate to a specific slide when the URL matches #. Although this feature is working, I am facing an issue with my pager-text not updating correctly when jumping to a specific slide. T ...

React/Javascript - Executing Function returns prematurely

I have been working on a function that takes an object and iterates through it to create a search query. However, the issue I'm facing is that the function returns before I finish looping through the object: export default function buildQuery(query) ...

Issue with JQuery mobile: dynamically inserted popup fails to appear

Can you help me troubleshoot an issue with my function? It's supposed to take a text string and output the text surrounded by '¬¬¬' in a button with a pop-up menu attached. The button looks fine, but when clicked, the popup ul list doesn& ...

Transferring information from a template to a view within Django

I am currently in the process of creating a bus reservation platform using Django. When a user searches for buses on a specific route, a list of available buses is displayed. Each bus in the list has a 'book' button that redirects to a new page c ...

Is it possible to apply fog to a specific area in Three.js instead of being dependent on the distance from

Picture this: a vast THREE.PlaneGeometry representing the floor with a camera placed at an arbitrary spot within the scene. By manually adjusting the near and far values of the fog, I can effectively conceal the outer edges of the plane to create the illu ...

Error: Trying to destructure a non-iterable instance is not valid. Non-array objects must implement a [Symbol.iterator]() in order to be iterable

Greetings to all who come across this query. I am currently working with Vue 2 and Firebase, aiming to retrieve a list of Arrays containing objects. I have successfully fetched the list from the real-time database in Firebase, but encountered an issue when ...

Error message "Property 'name' does not exist on type '{}'" is encountered when using Ionic/Angular HttpClient and no data type is specified

While working on my Ionic project, I encountered an error in Angular when trying to fetch data from an API using HttpClient. The error message that popped up was 'Property 'name' does not exist on type '{}'.'. Below is the cod ...

Confusion arises from the shadcn/vue configuration of Electron's source files

I currently have two src/components (assets) - one in the root directory and the other in /renderer. The component in the root directory was generated when I included resizable components using "npx shadcn-vue@latest add resizable" command, but something ...

Gather information from various Mongoose requests and forward it to the specified route

Recently embarked on my journey of learning Express with the help of online resources. While I have been able to handle pages dependent on a single query, I have hit a roadblock when it comes to creating a pagination table. In this scenario, I need to exec ...

Search as you type and populate multiple HTML form fields simultaneously

Up until now, I have been copy-pasting my way through this project! I possess a customer database and an HTML form for data entry. Upon clicking submit, it generates a fillable PDF on the server. While typing a name, results are displayed, and upon selec ...

Discover a Bootstrap form input integrated with a badge pill that can display changing data

Is there a way to ensure that the badge appears exactly where I want it to? Can you explain the CSS Class Parameters to me? Here is the current state of my HTML: <input id="<?php echo $this->id; ?>" type="<?php echo $th ...

Adjust alterations in a Vue Component to apply to separate routes

I have a Filter tab component that I use in various routes. When I click on a tab, it becomes active. After clicking on one tab, I want it to remain active in other routes as well. How can I achieve this? Any suggestions or articles would be greatly apprec ...

Exploring the dynamic data loading feature in Vue 3 by fetching data from the server and displaying it using a v-for

I am encountering an issue where I want to display data dynamically from a database using a v-for loop. However, when I attempt to push new data into the array, they show correctly in a console.log() but do not reflect any changes in the template. I have ...

Effective strategies for integrating Bootstrap and AngularJS without relying on jQuery

Currently exploring AngularJS after experimenting with Twitter's Bootstrap. I appreciate Bootstrap for its simplicity, sleek design, and mobile-friendliness. On the other hand, I have noticed a trend where people recommend using AngularJS over jQuery, ...

Update the ng-repeat attribute in HTML using either vanilla JavaScript or AngularJS

When the user clicks on the 'sort by book title' button, I want to change the ng-repeat="x in books' to ng-repeat="x in books|orderBy:'country'" in the HTML code. How can I achieve this action using JavaScript/Angular? Here is a ...

Difficulty clearing dictionary in Vue.js causing issues with the DOM not updating

Check out this jsfiddle example I am working with a dictionary in Vue and using a v-for loop to render its items. I want to clear the dictionary, but simply setting it to an empty object or deleting keys doesn't trigger a re-render in the DOM. Any su ...