Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vue.js: how to update state with object?

I am a newcomer to vue.js, but I have some experience with React from before.

I have read the vue guidelines and I am trying to understand vue through the concepts from React.

I assume that the vue data is similar to the React state, since when it updates the app it will render the page again.

So I would like to do something like ... (the code is in React)

this.setState(Object.assign({}, this.state, { key1: 'value1', key2 : 'value2'}))

But as far as I know, in vue:

this.key1 = 'value1';
this.key2 = 'value2';

Is that correct? I guess vue will render twice since it is 2 statements. How could I set multi values at once?

I have already tried ...

// not working
this.$set(Object.assign({}, thisRef.data, { key1: 'value1', key2: 'value2' }))

// not working
this.data = { key1 : 'value1', key2: 'value2' };

In the second one, data has changed - I have printed value with console.log(this)- , but it does not render again.

FYI, the full code from the vue template is here. Code review and correction will be very welcomed.

<script>
    export default {
        layout: 'reactQuickly'
        , data: function(){
            return {
                time: null
                , init: null
            }
        }
        , methods: {
            startTimer: function(time){
                clearInterval(this.init);
                let thisRef = this;
                let interval = setInterval(
                    function(){
                    console.log('2: Inside of interval', time)
                    let timeLeft = thisRef.time - 1;
                    if(timeLeft <= 0) clearInterval(interval);
                    thisRef.time = timeLeft;
                    // thisRef.$set(Object.assign({}, thisRef.data, { time: timeLeft }))
                    console.log('thisRef', thisRef, this);}
                , 1000);
                console.log('1: After interval');
                // this.data = { time : time, init: interval };
                this.time = time;
                this.init = interval;
                console.log('this.data', this.data);
                // this.$set(Object.assign({}, this.data, { time : time, init: interval}));
            }
        }
    }
</script>

============ Edition ===========

react this.state and vue this.data is not same, right? To me, main confusion starts from that point.

In vue

export default {
  data : function() {
    return {
      foo : 'bar'
    }
  }
}

and

In react

constructor() {
  super()
  this.state = {
    foo : 'bar'
  }
}

are completely different concept, right?

like image 580
Juneyoung Oh Avatar asked Aug 18 '18 02:08

Juneyoung Oh


2 Answers

Your concern in the question is not necessary. You don't need to worry about set multi values at once in Vue.

(I learnt what I am going to say from this awesome article => Updating UIs: value comparison VS mutation tracking.)

React and Vue has very different way of tracking when UI should be updated.

React uses Object Immutability. That's why everytime you setState, you essentially are creating a completely new object and the entire component rerender based on the new object values.

Vue uses getters/setters on the data object for mutation tracking. When you execute this.key1 = 'value1';, it will go through the setter of key1. In the setter, there's a function to notify the watcher and add this data changes to a queue.

In case you haven’t noticed yet, Vue performs DOM updates asynchronously. Whenever a data change is observed, it will open a queue and buffer all the data changes that happen in the same event loop. If the same watcher is triggered multiple times, it will be pushed into the queue only once. This buffered de-duplication is important in avoiding unnecessary calculations and DOM manipulations. https://v2.vuejs.org/v2/guide/reactivity.html#Async-Update-Queue

Therefore, when you execute this.key1 = 'value1'; this.key2 = 'value2';, it won't render twice. Vue automatically queues up the data changes and rerender them together later on.

What I meant to say is, your concern in the question is not necessary. You don't need to worry about set multi values at once in Vue. React and Vue has very different reactivity system. You might wanna read through the links above to get a better understanding.

(Btw, Vue uses getter/setter now but it will use JS proxy in the future. https://github.com/vuejs/roadmap)

like image 167
Jacob Goh Avatar answered Nov 02 '22 19:11

Jacob Goh


With $set method you can add only one property:

this.$set(this.existingObject, 'newProperty', 'value')

If you need to add multiple reactive properties, use assign method:

this.existingObject = Object.assign({}, this.existingObject, {
  newProperty1: 'value',
  newProperty2: 22
}

This is also useful when you want to change just some properties in object, without touching the rest of properties. So:

this.existingObject = Object.assign({}, this.existingObject, {
  existingProperty2: 'newValue',
  existingProperty6: 66
}

And if you are using Babel, and I assume you do, for both cases, adding or modifying some props, you can use latest spread feature:

let newOrExistingProps = {
  newProperty: 'value',
  existingProperty: 66
}

this.existingObject = {...this.existingObject, ...newOrExistingProps}
like image 23
Vladislav Ladicky Avatar answered Nov 02 '22 18:11

Vladislav Ladicky