简体   繁体   English

如何在`$emit('input')`完成更新之前修改VueJS中的'value' prop

[英]How to modify a 'value' prop in VueJS before `$emit('input')` finishes updating it

I have a question about creating VueJS components that are usable with v-model which utilise underlying value prop and $emit('input', newVal) .我有一个关于创建可与v-model一起使用的 VueJS 组件的问题,这些组件利用基础value prop 和$emit('input', newVal)

props: {
  value: Array
},
methods: {
  moveIdToIndex (id, newIndex) {
    const newArrayHead = this.value
      .slice(0, newIndex)
      .filter(_id => _id !== id)
    const newArrayTail = this.value
      .slice(newIndex)
      .filter(_id => _id !== id)
    const newArray = [...newArrayHead, id, ...newArrayTail]
    return this.updateArray(newArray)
  },
  updateArray (newArray) {
    this.$emit('input', newArray)
  }
}

In the above code sample, if I do two modifications in quick succession, they will both be executed onto the "old array" (the non-modified value prop).在上面的代码示例中,如果我快速连续进行两次修改,它们都将在“旧数组”(未修改的value prop)上执行。

moveIdToIndex('a', 4)
moveIdToIndex('b', 2)

In other words, I need to wait for the value to be updated via the $emit('input') in order for the second call to moveIdToIndex to use that already modified array.换句话说,我需要等待value通过$emit('input') ,以便第二次调用moveIdToIndex使用已修改的数组。

Bad solution 1错误的解决方案 1

One workaround is changing updateArray to:一种解决方法是将updateArray更改为:

  updateArray (newArray) {
    return new Promise((resolve, reject) => {
      this.$emit('input', newArray)
      this.$nextTick(resolve)
    })
  }

and execute like so:并像这样执行:

await moveIdToIndex('a', 4)
moveIdToIndex('b', 2)

But I do not want to do this, because I need to execute this action on an array of Ids and move them all to different locations at the same time.但我不想这样做,因为我需要对一组 Id 执行此操作,并将它们同时移动到不同的位置。 And await ing would greatly reduce performance.await会大大降低性能。

Bad solution 2错误的解决方案 2

A much better solution I found is to just do this:我发现一个更好的解决方案是这样做:

  updateArray (newArray) {
    this.value = newArray
    this.$emit('input', newArray)
  }

Then I don't need to wait for the $emit to complete at all.然后我根本不需要等待$emit完成。

However, in this case, VueJS gives a console error:但是,在这种情况下,VueJS 给出了控制台错误:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.避免直接改变 prop,因为每当父组件重新渲染时,值都会被覆盖。 Instead, use a data or computed property based on the prop's value.相反,根据道具的值使用数据或计算属性。 Prop being mutated: "value"道具被变异:“价值”

Does anyone have any better solution?有没有人有更好的解决方案?

OK.好的。 These are your options as far as I understand your use case and application.据我了解您的用例和应用程序,这些是您的选择。

First of all, don't mutate the props directly save the props internally and then modify that value.首先,不要直接修改 props 内部保存 props 然后修改那个值。

props: {
  value: Array
},
data() {
  return {
    val: this.value
  }
}

If the next modification to the array is dependent on the previous modification to the array you can't perform them simultaneously.如果对阵列的下一次修改依赖于对阵列的先前修改,则您不能同时执行它们。 But you need it to happen fairly quickly ( i will assume that you want the user to feel that it's happening quickly ).但是您需要它发生得相当快(我假设您希望用户感觉它发生得很快)。 What you can do is perform the modification on the val inside the component and not make it dependent on the prop.您可以做的是对组件内部的val执行修改,而不是使其依赖于 prop。 The val variable is only initialized when the component is mounted. val变量仅在安装组件时初始化。 This way you can modify the data instantly in the UI and let the database update in the background.通过这种方式,您可以立即在 UI 中修改数据,并让数据库在后台更新。

In other words, your complete solution would look like this:换句话说,您的完整解决方案如下所示:

props: {
  value: Array
},
data () {
  return {val: this.value}
},
methods: {
  moveIdToIndex (id, newIndex) {
    const newArrayHead = this.val
      .slice(0, newIndex)
      .filter(_id => _id !== id)
    const newArrayTail = this.val
      .slice(newIndex)
      .filter(_id => _id !== id)
    const newArray = [...newArrayHead, id, ...newArrayTail]
    return this.updateArray(newArray)
  },
  updateArray (newArray) {
    this.val = newArray
    this.$emit('input', newArray)
  }
}

This solution fixes your problem and allows you to execute moveIdToIndex in quick succession without having to await anything.此解决方案解决了您的问题,并允许您快速连续执行moveIdToIndex ,而无需等待任何内容。

Now if the array is used in many places in the application next best thing would be to move it to a store and use it as a single point of truth and update that and use that to update your component.现在,如果数组在应用程序的许多地方使用,那么接下来最好的事情就是将它移动到一个商店并将其用作单一事实点并更新它并使用它来更新您的组件。 Your state will update quickly not simultaneously and then defer the update to the database for a suitable time.您的状态将不会同时快速更新,然后将更新推迟到合适的时间。

  1. Emit a message to the parent to change the prop. Emit消息的母体改变道具。
  2. Put a watcher on the prop (in the child) and put your code to use the new value there.watcher放在道具上(在孩子中)并将您的代码放在那里以使用新值。

This keeps the child from mutating the data it does not own, and allows it to avoid using nextTick .这可以防止孩子改变它不拥有的数据,并允许它避免使用nextTick Now your code is asynchronous and reactive, without relying on non-deterministic delays.现在您的代码是异步和反应式的,无需依赖非确定性延迟。

How about making copy of the value ?复制value怎么样?

moveIdToIndex (id, newIndex) {
    const valueCopy = [...this.value]
    const newArrayHead = this.valueCopy
      .slice(0, newIndex)
      .filter(_id => _id !== id)
    const newArrayTail = this.valueCopy
      .slice(newIndex)
      .filter(_id => _id !== id)
    const newArray = [...newArrayHead, id, ...newArrayTail]
    return this.updateArray(newArray)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM