Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The $refs maybe undefined #9856

Closed
underfin opened this Issue Apr 10, 2019 · 9 comments

Comments

Projects
None yet
4 participants
@underfin
Copy link

underfin commented Apr 10, 2019

Version

2.6.10

Reproduction link

https://codesandbox.io/embed/3q34mqqwxm

Steps to reproduce

  1. In the template not ref use.
  2. In the ts use readonly $refs!: { div: Element };
  3. In the runtime this.div.xx will throw error.

What is expected?

Throw error in compiler-time.

What is actually happening?

Throw error in runtime.


we can use readonly $refs!: { div: Element | undefined }; and this.div!.xx .

underfin added a commit to underfin/vue that referenced this issue Apr 10, 2019

@HyunmoAhn

This comment has been minimized.

Copy link

HyunmoAhn commented Apr 12, 2019

I have same Issue.

Version

2.6.3

Description

Sometimes, when some of each refresh page, mounted lifecycle of component can't get this.$ref because it is before render.

I know mounted doesn't guarantee completed about render.
But It is caused child component but I am not associate with child component.

And, It is occurred randomly!

Example Code

// template
<template>
  <div class="asset_box" ref="assetBox">
    <span class="amount" ref="amount">
      amount
    </span>
    <div class="gauge_box">
      <span class="blind">asset gauge</span>
      <div class="gauge" ref="gauge"></div>
    </div>
   ... ellipsis
    <error-one-coin v-if="isShowError"></error-one-coin> // child component but doesn't associate with $ref
  </div>
</template>

// mounted
mounted () {
    console.log(this.$refs, document.querySelector('.asset_box'), document.querySelector('.amount'), 
},

Analysis

I got braking point on mounted code like below capture.
image

normal case.

image

  • console of chrome browser on breakpoint
    image

.asset_box is target component that has mounted lifecycle.

error case

image

  • console of chrome browser on breakpoint
    image

The location that .asset_box has comment like <!---->

If you need more information, I comment more.

Please Tell whether it is bug or not.
Thank you.

@underfin

This comment has been minimized.

Copy link
Author

underfin commented Apr 12, 2019

@HyunmoAhn . Can you provider all code in your case? My problem is related with $refs type defined.

@LinusBorg

This comment has been minimized.

Copy link
Member

LinusBorg commented Apr 15, 2019

@underfin I fail to see what you think wrong about the behaviour you are showing. There's no element to reference, so $refs is empty.

It seems you think you should be able to overwrite $refs with a permanent value. That's not the case. $refs is created dynamically during runtime, and will always only contain the refs that are currently found in the document.

@HyunmoAhn Your problem seems unrelated. The <!-- --> comment in the page's markup indicates that you have a render error, which is why there's no ref: Because of the render error, no element was created, so there's no element for the ref to reference.

@LinusBorg LinusBorg closed this Apr 15, 2019

@underfin

This comment has been minimized.

Copy link
Author

underfin commented Apr 15, 2019

@LinusBorg , my case only its $refs maybe undefined. Now, I change my code in https://codesandbox.io/embed/3q34mqqwxm. As you say,$refs is created dynamically during runtime, the same maybe removed during runtime.So, I think its type should include undefined.
Also, its path src/core/vdom/modules/ref.js with code, refs[key] = undefined.

export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) {
const key = vnode.data.ref
if (!isDef(key)) return

const vm = vnode.context
const ref = vnode.componentInstance || vnode.elm
const refs = vm.$refs
if (isRemoval) {
if (Array.isArray(refs[key])) {
remove(refs[key], ref)
} else if (refs[key] === ref) {
refs[key] = undefined
}
} else {
if (vnode.data.refInFor) {
if (!Array.isArray(refs[key])) {
refs[key] = [ref]
} else if (refs[key].indexOf(ref) < 0) {
// $flow-disable-line
refs[key].push(ref)
}
} else {
refs[key] = ref
} } }`

@LinusBorg

This comment has been minimized.

Copy link
Member

LinusBorg commented Apr 15, 2019

$refs isn't undefined in your sandbox, it never is. It's always an object. It just may be an empty object sometimes, so why this.$refs.div may be undefined.

But the types already deflect that.

@underfin

This comment has been minimized.

Copy link
Author

underfin commented Apr 16, 2019

@LinusBorg, yea, its right, I misstated it with this.$refs.div to this,$refs.Can you reopen the issuse with rename, I want to request pr to fix it.

@LinusBorg

This comment has been minimized.

Copy link
Member

LinusBorg commented Apr 16, 2019

I still don't understand what you want to fix, though.

@underfin

This comment has been minimized.

Copy link
Author

underfin commented Apr 16, 2019

@LinusBorg . readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] }; $refs is defined on now. The type with $refs should defined readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] | undefined };.

@pikax

This comment has been minimized.

Copy link
Contributor

pikax commented Apr 17, 2019

From what I understand the OP is referring to typescript only, because the type doesn't get automatically picked up by the compiler when he does

console.log(this.$refs.div.nodeType); // compiler can't resolve the correct type and `nodeType` show an error

Is not vuejs fault, you need to cast your element to the type you want to infer to typescript

console.log((this.$refs.div as Element).nodeType);

I recommend you check Type Guards and Differentiating Types

It might be useful to add the undefined to Vue.$refs interface

 readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] | undefined };

In regards to runtime error, you should always check if the ref is not undefined and if is an instance or an array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.