Subscribe on changes!

v-memo v-for with inline array iteration error "can't access property "el", oldVNode is undefined"

avatar
Aug 4th 2021

Version

3.2.0-beta.7

Reproduction link

sfc playground

this is inline example that does not work an raise the error

// [1,2,3] inline array
v-for="v,k in [1,2,3]"

the following will work as expected ( when arr = [1,2,3] )

v-for="v,k in arr"

Steps to reproduce

open sfc

  1. click inc button 3 times, when v-memo trigger a change the error will show.

What is expected?

no error

What is actually happening?

Uncaught (in promise): can't access property "el", oldVNode is undefined
avatar
Aug 5th 2021

I noticed another bug in your example. If you declare an array using const instead of let/var then following error occurs

Uncaught (in promise): Cannot read property 'emitsOptions' of null

avatar
Aug 5th 2021

are you sure its not the error you get on the 4th click?

avatar
Aug 5th 2021

ahh yes, it shows up on 4th click, nevermind then

avatar
Aug 6th 2021

the root cause is:

return (_ctx, _cache) => {
  return (_openBlock(), _createElementBlock(_Fragment, null, [
    _createElementVNode("button", {
      onClick: _cache[0] || (_cache[0] = $event => (count.value++))
    }, " inc " + _toDisplayString(count.value), 1 /* TEXT */),
    (_openBlock(), _createElementBlock(_Fragment, null, _renderList([1,2,3], (id, index, ___, _cached) => {
      const _memo = ([count.value < 3 ? true : count.value])

+ 	  we will lose the `dynamicChidren` (the `currentBlock` is empty) because return the cached vnode here.
      if (_cached && _cached.key === id && _isMemoSame(_cached.memo, _memo)) return _cached
      const _item = _createVNode(__import_1__.default, {
        count: count.value,
        index: index,
        key: id
      }, null, 8 /* PROPS */, ["count", "index"])
      _item.memo = _memo
      return _item
    }, _cache, 1), 64 /* STABLE_FRAGMENT */))
  ], 64 /* STABLE_FRAGMENT */))
}
avatar
Aug 6th 2021

it will be interesting to see if this also related to this root cause https://github.com/vuejs/vue-next/issues/4262

avatar
Aug 6th 2021

@lidlanca

withMemo works fine because it catches the block tree.

see :

export function withMemo(
  memo: any[],
  render: () => VNode<any, any>,
  cache: any[],
  index: number
) {
  const cached = cache[index] as VNode | undefined
  if (cached && isMemoSame(cached.memo!, memo)) {
    // make sure to let parent block track it when returning cached
    if (isBlockTreeEnabled > 0 && currentBlock) {
      currentBlock.push(cached)
    }
    return cached
  }
  const ret = render()
  ret.memo = memo
  return (cache[index] = ret)
}