Is it possible to make any object reactive within Vuex?

Seeking ways to enhance the sorting of normalized objects based on a relationship. Imagine having an application that requires organizing data in a Vuex store containing multiple normalized objects, like this:

state: {
    worms: {
        3: { id: 3, name: 'Slurms McKenzie', measurements: [1, 6, 9]  },
        4: { id: 4, name: 'Memory worm',  measurements: [3, 4, 12] },
        6: { id: 6, name: 'Alaskan Bull Worm', measurements: [5, 7, 14]},
        ...
    },
    measurements: {
        1: { id: 1, length: 5.2, timestamp: ...},
        2: { id: 2, length: 3.4, timestamp: ...},
        3: { id: 3, length: 5.4, timestamp: ...},
        ...
    },
};

For instance, if there's a need to sort the worms based on the timestamp of their highest length achievement. To leverage Vue's reactivity, defining a getter for each worm would be ideal, like so:

const getters = {
    longestLength: {
        get() { return $store.getters
                    .measurements(this.measurements)
                    .sort(...)[0] },
     },

     timestampForLongest: {
         get() { return this.longestLength.timestamp }
}

worm.extend(getters);

This setup allows for quick sorting based on timestampForLongest, assuming the value is cached.

While there's a good starting point for calling this extend method, several challenges exist:

  1. Current approach involves computing denormalized map and subsequent sorting, resulting in around 700ms latency on my Chrome browser with an 8th gen Intel processor; aiming to reduce this delay.
  2. Uncertainty surrounds manual invocation of Vue's reactivity system, potentially requiring definition of getters that trigger something like measurement.__ob__.dep.depend().
  3. Concern about Vue's capability to efficiently handle 800+ rows due to performance limitations or private API usage subject to changes.
  4. Finding a way to maintain the Vuex store ($store) within scope of getters, possibly resorting to arrow functions for clarity.

Is it feasible to compute and cache values as needed within plain JavaScript objects using Vue?

Answer №1

I hope this aligns somewhat with your original vision.

If I've grasped the concept correctly, you were aiming to establish 'computed properties' (or getters of sorts) named longestLength and timestampForLongest for each worm. These properties would be based on the measurements within the state.

To tackle this, I've transformed each worm into a Vue instance. While this approach involves other functionalities provided by a Vue instance that are unnecessary here, Vue 2 lacks the ability to selectively extract specific components. Vue 3 might offer more modularity in this aspect, as per rumors. The key components we require are observable data (feasible through Vue.observable) and computed properties (solely accessible via a Vue instance). As a sidenote, this mirrors how Vuex operates behind the scenes—by creating a distinct Vue instance and linking it to the data, computed functions, etc..

The subsequent code may appear lengthy; however, a significant portion is dedicated to generating appropriate test data. Initially, data with nested measurements under the worms is generated before restructuring it conforming to your specified format within my mutation function. Prior to being added to the state, every entry inside worms is converted into a Vue instance.

To highlight critical sections amidst the complexity, I've marked them with // This bit is important comments.

Answer №2

Here's a unique approach for you to consider: 1. Steer clear of sorting whenever possible 2. Opt for storing properties such as max_length and max_time within each worm object, updating them with new observations instead

This method eliminates the need for sorting each time.

Answer №3

The code you gave contains syntax errors that needed to be fixed:

const states = {
  worms: {
    3: {
      id: 3,
      name: 'Slurms McKenzie',
      measurements: [1, 6, 9]
    },
    4: {
      id: 4,
      name: 'Memory worm',
      measurements: [3, 4, 12]
    },
    6: {
      id: 6,
      name: 'Alaskan Bull Worm',
      measurements: [5, 7, 14]
    }
  },
  measurements: {
    1: {
      id: 1,
      length: 5.2,
      timestamp: 'ts1'
    },
    2: {
      id: 2,
      length: 3.4,
      timestamp: 'ts2'
    },
    3: {
      id: 3,
      length: 5.4,
      timestamp: 'ts3'
    },
  }
}

const store = new Vuex.Store({
  state: states,
  getters: {
    getWorms: state => {
      return state.worms
    },
    getLongestLengthByMeasurementId: state => ids => {
      const mapped = ids.map(id => {
        const measurement = state.measurements[id]
        if (measurement) {
          return {
            length: measurement.length || 0,
            timestamp: measurement.timestamp || 0
          }
        } else {
          return {
            length: 0,
            timestamp: 0
          }
        }
      })
      return mapped.find(item => item.length === Math.max.apply(null, mapped.map(item => item.length))).timestamp
    }
  },
  mutations: {
    // setting timestamp in store.state.worms[wormId]
    setLongestLength(state, wormId) {
      if (state.worms[wormId] && typeof state.worms[wormId].timestamp !== 'undefined') {
        // update the timestamp
      } else {
        // get and set the timestamp 
        const ts = store.getters.getLongestLengthByMeasurementId(state.worms[wormId].measurements)
        Vue.set(state.worms[wormId], 'timestamp', ts)
      }
    },
  },
  actions: {
    // set timestamp worm by worm
    setLongestLength({
      commit
    }, wormId) {
      Object.keys(store.getters.getWorms).forEach(key =>
        commit('setLongestLength', parseInt(key, 10))
      )
    }
  }
})

const app = new Vue({
  store,
  el: '#app',
  mounted() {
    store.dispatch('setLongestLength')
    console.log('worms', store.state.worms)
  }
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
  <div v-for="worm in $store.state.worms">Timestamp by worm (ID {{worm.id}}): {{worm.timestamp}}</div>
</div>

To have a more efficient getter with both a get and set function, simply add get: before your getter function.

Answer №4

When I develop a Vue application with a substantial amount of data similar to yours, my approach usually involves organizing the data like this:

const vm = new Vue({
  data() {
    return {
      worms: [
        {id: 1,name: "Slurms McKenzie",measurements: [1, 6, 9]},
        {id: 2,name: "Memory worm",measurements: [3, 4, 12]},
        {id: 3,name: "Alaskan Bull Worm",measurements: [5, 7, 14]}
      ],

      measurements: [
        {id: 1,length: 5.2,timestamp: 123},
        {id: 2,length: 3.4,timestamp: 456},
        {id: 3,length: 5.4,timestamp: 789}
      ]
    };
  },

  computed: {
    sortedByLength() {
      return [...this.measurements]
        .sort((a, b) => a.length - b.length)
        .map(measurement => measurement.id)
        .map(id => this.worms.find(worm => worm.id === id));
    },

    timestampForLongest() {
      return this.sortedByLength[0].timestamp;
    }
  }
});

In Vue, computed properties are automatically updated when they change and cached otherwise. Adapting this logic into state/getters for Vuex follows similar principles.

Storing data as arrays is more manageable than using objects. In cases where you need to work with objects, tools like lodash can assist in sorting without significant complications.

Answer №5

Perhaps this solution could be helpful. Without knowing the full structure of your project, I have attempted to recreate it here using one possible approach.

Your project includes a state that consists of worms and measurements lists. Each individual worm has a list of indexes for measurements, which are presumably associated with the main measurements list.

The state should be defined within the Vuex store. In this case, your store will contain four key elements: state, getters, actions, and mutations.

The state essentially serves as the single source of truth for the entire application. The next question is: how can components and routes access the data stored in our state? Well, the getters play a crucial role by retrieving data from the store and providing it to the components. Specifically, we are interested in fetching the sortedByTSDec and sortedByTSAsc methods.

Having understood how to retrieve data from the state, let's explore how to set data into the state. While one might expect to define setters, in Vuex these are referred to as mutations. By defining a mutation, you can effectively update the data in the state.

Additionally, actions in Vuex are similar to mutations, but they do not directly modify the state; instead, they trigger mutations. Think of actions as asynchronous functions and mutations as synchronous ones.

For instance, the generateData action might be responsible for retrieving data, possibly from an external server or database, and then calling the populate mutation to update the state.

What about the Worn class?

This is where things get interesting. Using the Vue.extend() method enables the creation of a subclass of the base Vue constructor. Why? Because this subclass includes a data option. When the populate mutation assigns the generated data to a new instance of Worn, the state.worms ends up containing a collection of Worn objects.

In addition, computed properties are declared to calculate values such as longestLength and timestampForLongest based on the data available within each instance.

To sort the list of worms according to the timestamp of their longest length, you would first find the longest lengths and then utilize the .sort() method along with a custom compare function to achieve the desired order.

A basic snippet illustrating this process:

. . . (Remaining code block omitted for brevity) . . .

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

Is it possible to use a hash map to monitor progress while looping through this array in JavaScript?

I've been exploring some algorithmic problems and I'm puzzled about the most efficient way to solve this particular question. While nested for loops are an option, they don't seem like the optimal choice. I'm considering using a hash ma ...

Redux's 'connect' function fails to recognize changes in the state array

I've recently implemented redux with a reducer that handles an array of time slots for a specific date. Whenever the date is changed, the reducer successfully updates the state (confirmed through console logs in my mapStateToProps function). However, ...

Effortless Numerical Calculation for Trio Input Fields

I am in the process of setting up three numeric input fields that will automatically calculate and display results on the side. I am struggling with this task as I am not familiar with using ajax. Can someone assist me in implementing this functionality us ...

"JavaScript: Issue Encountered While Converting Hexadecimal to Decimal

I've been working on a custom function to convert hexadecimal to decimal in my Scratch project: function Hex2Decimal(hex){ var deci = 0; var num = 1; var hexstr = String(hex); hexstr = hexstr.toLowerCase(); var expon = 0; for( ...

Iterate through each row asynchronously, waiting for each one to complete before moving on to the

Trying to navigate through multiple pages using puppeteer has been successful, except when attempting to parse through them at the same time. The issue seems to stem from the code executing async operations in rapid succession, overwhelming the browser. My ...

Columns that can be resized in a right-to-left (

Whenever I use RTL, the columns behave erratically when resizing... I have implemented colResizable. You can see an example here: http://jsfiddle.net/r0rfrhb7/ $("#nonFixedSample").colResizable({ fixed: false, liveDrag: true, gripInnerHtml: "<d ...

Unable to utilize the post method in Node.js

<form class="ui form" action"/submit" method="post"> <div class="field"> <label>Time dedicated to studying:</label> <input type="number" name="study" placeholder="Total time spent on studies:"> ...

Removing an element in JSON is resulting in an element that is undefined

Dealing with a JSON Object, I am faced with a challenge where deleting elements results in undefined entries, causing issues in my usage within datatables. Here is my current approach: function dTable(presId){ var allregos = '[{"presId": "a09N0000 ...

Guide for extracting the first matching group with regex in node.js

Extracting a specific value from a string in a text file can be tricky. In the example below, the goal is to get the value of valuetext: <CONFIG_entry name="Konfiguration:Allgemeine Einstellungen:CMS-Server:Port" valuetext="15000" value="15000" adr="CL ...

Ways to transition into a developer role

I'm currently studying Javascript and Dynamic HTML as part of a course, and while I'm not encountering any errors or warnings in Firefox and Chrome, I believe there might be some issues with my code. If anyone would be willing to take a look and ...

To properly format the date value from the ngModel in Angular before sending it to the payload, I require the date to be in the format

When working with Angular 9, I am facing an issue where I need to format and send my date in a specific way within the payload. Currently, the code is sending the date in this format: otgStartDate: 2021-07-20T09:56:39.000Z, but I actually want it to be for ...

HTML5: Enhancing Video Playback on the iPad with Custom Zoom Controls

I've customized a smaller UIWebView specifically for the iPad, and I've created my own HTML5 controls for the video playback. However, when I try to maximize the video, all I see is a black screen instead of the actual content. The audio still pl ...

Tips for selecting the initial and final elements of a specific class

Here is a simple example of table markup: <div class="table"> <div class="table__headers"></div> <div class="table__row"></div> <div class="table__row"></div> </div& ...

Using Javascript to trigger a setTimeout function after the user's mouse leaves

There is a div that pops up when the user's mouse exits the webpage, containing a survey pertaining to my website. I want to avoid prompting users to take the survey if they move their cursor out of the page within the first few minutes of browsing. ...

Basic AngularJS application, however I am receiving {{this is supposed to be the information}}

Building an angularjs app I have set up an asp.net mvc4 application and integrated the angularjs package using nuget. The Layout.cshtml file has been updated to look like this: <!DOCTYPE html> <html ng-app="myApp"> <head> <meta ...

Set up the configuration for express to access an external API through proxy.conf.json while in production mode

I am currently managing two applications on Heroku, one being myserverapi (built with Spring Boot) and the other being a client-side Angular app named client. The server is hosted at myserver.heroku.com while the client resides at myclient.heroku.com. At t ...

Retrieving the current user using Vue/Axios within the Laravel framework

Currently, I am looking for the most efficient way to load the current user into my Vue app in order to access the username and ID from any component or route. At this point, I have attempted the following approach: app.js new Vue({ el: '#app&apos ...

Is it possible to adjust the color and placement of the CircularProgress component?

Utilizing the CircularProgress component provided by Material has been a goal of mine. In my efforts to achieve this, I developed a component with the intention of customizing its color: import React, { Component } from 'react'; import { withSt ...

Can you explain how I can declare a variable to store a scraped element in Puppeteer?

const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: false, defaultViewport: null }) const page = await browser.newPage() await page.goto('https://www.supre ...

What is the feature of a Vue.js component that removes other sibling markup?

While diving into the world of Vue, I encountered an interesting behavior that has left me wondering. I have my main markup set up and realized that a part of it could work as a separate component. However, when I tried adding this component to the contain ...