vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.36k stars 8.29k forks source link

v-for with ref not working as suggested in documentation in some scenarios #3418

Open bazuka5801 opened 3 years ago

bazuka5801 commented 3 years ago

Version

3.0.7

Steps to reproduce

Original Docs:

import { onBeforeUpdate, onUpdated } from 'vue'

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el => {
      if (el) {
        itemRefs.push(el)
      }
    }
    onBeforeUpdate(() => {
      itemRefs = []
    })
    onUpdated(() => {
      console.log(itemRefs)
    })
    return {
      setItemRef
    }
  }
}

What is expected?

Filled array for component lifetime

What is actually happening?

After component update array is empty, but lifetime is continue.


Temporary solution: Tip: Only use and export. Authors: @NataliaTepluhina

function useListRef<T>() {
  const elements = ref<T[]>([])
  const active = ref<boolean>(false)

  watch(active, (isActive) => {
    if (isActive) {
      nextTick(() => active.value = false)
    }
  })

  function addRef(el) {
    if (!active.value) {
      elements.value = []
      active.value = true
    }
    el && elements.value.push(el)
  }

  return {
    elements,
    addRef,
  }
}
posva commented 3 years ago

Please provide a boiled down issue like asked in the new issue helper. It contains a js fiddle you should use. Closing until one is provided

LinusBorg commented 3 years ago

@posva I reopened as I'm familiar with the issue myself. Already started talking to some of our team how to best resolve this.

It's not strictly a bug as it's more of a conceptual blind spot we have to fill somehow. the recommended approach from the docs can fail under certain circumstances, i.e. this:

<template>
  <child-component>
    <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
      {{ item }}
    </div>
  </child-component>
</template>

<script>
  import { ref, reactive, onMounted, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      onMounted(() => list.push(4))

      // this will not run when `list` is mutated, 
      // because `list` is a slot dependency, tracked by the `child`, not this parent.
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>
bazuka5801 commented 3 years ago

@posva I was trying to use codepen, codesandbox, I dont understand these instruments, sorry, but it is really don't working from docs, who is writing doc page with "v-for + article"?

bazuka5801 commented 3 years ago

https://github.com/vuejs/docs-next/blame/master/src/guide/migration/array-refs.md For avoid these confuses we need test docs examples, it is possible?))

bazuka5801 commented 3 years ago

Because In my opinion, The system is to blame, not the people and the system needs to be improved

oleksiikhr commented 3 years ago

Yes, I also have such a problem. This watch for :ref only works on the penultimate push.

https://codesandbox.io/s/vue3-refs-q6g6p?file=/src/App.vue

LinusBorg commented 3 years ago

@bazuka5801 Just to be clear: the docs example isn't generally failing. it's failing under specific conditions that you guys must have encountered (can't say more as you couldn't provide a reproduction). Hence a test wouldn't really have helped.

In can't talk to your specific situation as you never provided a complete example in the issue. I provided an example of specific circumstance under which it's failing and a short explanation as to why.

As i write, it's a conceptual issue we have to address and it's not as straightforward as it may seem.

bazuka5801 commented 3 years ago

@LinusBorg in most projects, public components this "specific conditions" used. It should be fixed.

LinusBorg commented 3 years ago

It should be fixed.

Did any of us say anything to the contrary?

bazuka5801 commented 3 years ago

It should be fixed.

Did any of us say anything to the contrary?

No.