zoukankan      html  css  js  c++  java
  • Caution using watchers for objects in Vue

    Caution using watchers for objects in Vue

    Suppose we have an object and we want to do something when it’s properties change. Probably we’ll start with something like that:

    export default {
    data: () {
    return {
    obj: {
    prop: 1
    }
    }
    },
    watch: {
    obj: {
    deep: true, // <!-- This is important;
    handler () {
    ...
    }
    }
    }
    }

    deep modifier is important here since Vue does not compare nested object properties by default. And one may feel a temptation to use deep everywhere.

    This short article admonishes you against doing that. Even more, I feel that my own code became more predictable and stable since I stopped using deep completely (and stopped watching objects at all).

    Actually, the caveat about watching objects is that it’s hard to predict the behavior even for merely complex code. And it’s not about Vue — that’s just a way javascript works. I mean,

    var a = {prop: 1}
    var b = {prop: 1}
    console.log(a === b) // <!-- FALSEvar c = {prop: 1}
    var d = c
    d.prop = 2
    console.log(c === d, c.prop) // <!-- TRUE, 2

    So, imagine we have some array of items in Vuex store. Suppose, one of them can be selected. And we don’t store selected property inside the item object itself, because it’s not optimal, and because we want to use normalizr approach. That’s how the store could look like:

    const store = new Vuex.Store({
    state: {
    items: [{id: 1, name: 'First'}, {id: 2, name: 'Second'}],
    selectedItemId: 1
    },
    mutations: {
    selectItem (state, id) {
    state.selectedItemId = id
    }
    }
    })

    But it’s convenient to have isSelected property, that’s why we add the following getter:

    items (state) {
    return state.items.map(item => ({
    ...item,
    isSelected: item.id === state.selectedItemId
    }))
    }

    Notice spread syntax here. We can not (and we should not) affect the item, stored in the state, that’s why we create new objects here. And items.map also creates a new array.

    Ok, now imagine that the items can be reordered, and we want the order to be stored in cookies. We add the following watcher:

    computed: {
    items () { return this.$store.getters.items }
    },
    watch: {
    items (items) {
    const ids = items.map(item => item.id)
    console.log('Storing ids..., ids)
    }
    }

    Here you can play with the code: https://jsfiddle.net/kasheftin/nu5aezx1/15/

    Notice that items watcher runs every time after changing selectedItemId. This happens because selectedItemId triggers items getter to rebuild, and the last returns the new array. It’s so simple and convenient to clone an object using array functions and ES6 syntax that we do it constantly, and that’s why the watcher can be triggered much more often then it should. Even if we do

    computed: {
    items () { return this.$store.getters.items },
    itemIds () { return this.items.map(item => item.id) }
    },
    watch: {
    itemIds (itemIds) {
    console.log('Storing ids...', itemIds)
    }
    }

    This is not the correct code as well. Intuition says that here we just extract ids from items, and the watcher should not trigger after selectedItemId change. But it does. items.map produces the new array every time.

    Solution

    Just use JSON.stringify:). If the object has circular dependencies, this one works just great: https://www.npmjs.com/package/circular-json-es6. Use it like this:

    computed: {
    items () { return this.$store.getters.items },
    itemIdsTrigger () {
    return JSON.stringify(this.items.map(item => item.id))
    // For this simle case can be replaced with:
    // return this.items.map(item => item.id).join(',)
    }
    },
    watch: {
    itemIdsTrigger () {
    // We don't need itemIdsTrigger value itself;
    // We don't extract it with JSON.parse;
    // Just use initial this.items;
    console.log('items order changed', this.items)
    }
    }
  • 相关阅读:
    数据结构与算法——优先队列类的C++实现(二叉堆)
    Effective C++--经验条款
    [精]读览天下免费阅读平台
    团队现状与用人标准——揭秘万达电商(6)
    稀疏向量计算优化小结
    漫谈雪崩
    Git起步
    Solr 配置文件之schema.xml
    Shader toy (顺手写两个Gyro)(纯代码写3D)
    Tomcat服务器安装
  • 原文地址:https://www.cnblogs.com/chucklu/p/14242977.html
Copyright © 2011-2022 走看看