vue.common.js: Uncaught TypeError: Cannot read property 'toLowerCase' of undefined (on node <!---->)

Created on 4 Oct 2016  路  7Comments  路  Source: vuejs/vue

Seems to happen if I assign a value to a component data property from within the beforeMount() method. The component references the data object like:

 <div v-if="user != null">
      Hello.
 </div>

beforeMount() {
      this.user = this.$store.state.user;
}

Error is being thrown inside vue.common.js, line 4089:

  function assertNodeMatch (node, vnode) {
    if (vnode.tag) {
      console.log("assetNodeMatch", node, vnode);

      return (
        vnode.tag.indexOf('vue-component') === 0 ||
        vnode.tag === nodeOps.tagName(node).toLowerCase() // <----- thrown here
      )
    } else {
      return _toString(vnode.text) === node.data
    }
  }

It's interesting to note the error is only thrown when I actually assign this.user within beforeMount(). If I leave the v-if reference in the template, but don't assign anything to the property ever, nothing is thrown.
The console.log I put here shows this right before the error is thrown:

assetNodeMatch VNode {tag: "div", data: undefined, children: Array[1], text: undefined, elm: undefined鈥

So it's seeing the node as plain '', and then nodeOps.tagName can't find a .tagName property on this given node, so it throws the error. This node is generated by Vue, but I'm not sure why. Maybe it's if the server and browser template state is not matching (I'm using SSR), but regardless, this error should maybe be handled differently.
Anyone have any suggestions?

Most helpful comment

While I not really know the hydration part of the codebase very well, it seems to me that what you are doing is asking for your App to break.

  • you are rendering the component on the server with someData: null
  • therefore, that div is not rendered on the server (because of the v-if).
  • then, when the App is booting on the client, you _change_ the state of the component to someData: {test: 'test'}
  • you do this in created, so _before_ the component's virtualDOM is being rendered.

This essentially means that the server didn't render the div, but the virtualDOM on the client expects it to exist.

And the reason that it does not happen when you do it in mounted is that in this case, you change the component's state _after_ initial client-side render of the virtualDOM.

So the virtualDOM renders _without_ the div (like on the server), and then you change the components state, triggering a re-render that includes the div now.

All 7 comments

Seemed to be because I was setting user on the main app component, and also a component underneath, but the component still thought the element was there, which seems weird.

I keep experiencing this in seemingly simple cases.
If I have a template which tries to access a data property as such:

    <div v-if="someData">
      <div>{{someData}}</div>
    </div>

This just wants to render the data if it exists.
Then in my component, I have:

    data() {
      return {
        someData: null
      }
    },

    created() {
      if(typeof window != 'undefined') {
        this.someData = {test: 'test'}
      }
    }

However, setting someData in any other method (besides the initial data() property) causes the error to be thrown. Does this happen for anyone else?

client.js:4261 Uncaught TypeError: Cannot read property 'toLowerCase' of undefined
    at assertNodeMatch (http://localhost:8000/client.js:4261:45)
    at hydrate (http://localhost:8000/client.js:4204:13)
    at hydrate (http://localhost:8000/client.js:4232:21)
    at VueComponent.patch [as __patch__] (http://localhost:8000/client.js:4296:18)
    at VueComponent.Vue._update (http://localhost:8000/client.js:1795:20)
    at VueComponent.<anonymous> (http://localhost:8000/client.js:1770:11)
    at Watcher.get (http://localhost:8000/client.js:813:28)
    at new Watcher (http://localhost:8000/client.js:805:13)
    at VueComponent.Vue._mount (http://localhost:8000/client.js:1769:20)
    at VueComponent.Vue$2.$mount (http://localhost:8000/client.js:5669:16)

@yyx990803 This seems like a bug in Vue, either by improper error handling or something on my end that maybe I'm not doing correctly, but it seems pretty straightforward. It is because it is seeing '' as an individual node. I think this is rendered when the server-side rendering does not match the client-side, which seems to be verified if I remove the 'if(typeof window != 'undefined')' check on the created() method, and just set the data on both client and server. Then, everything matches and no error is thrown. Is there a way around this?

Update: no error is produced if I set the data within the mounted() method. Is there a reason this has to be this way in the scenario of server-side rendering coupled with the client-side re-render? I've seen other projects try and prefetch data, and it wouldn't seem out of the ordinary to set data within the created() or activated() or beforeMount() methods, but it is throwing the error when I try to do that.

While I not really know the hydration part of the codebase very well, it seems to me that what you are doing is asking for your App to break.

  • you are rendering the component on the server with someData: null
  • therefore, that div is not rendered on the server (because of the v-if).
  • then, when the App is booting on the client, you _change_ the state of the component to someData: {test: 'test'}
  • you do this in created, so _before_ the component's virtualDOM is being rendered.

This essentially means that the server didn't render the div, but the virtualDOM on the client expects it to exist.

And the reason that it does not happen when you do it in mounted is that in this case, you change the component's state _after_ initial client-side render of the virtualDOM.

So the virtualDOM renders _without_ the div (like on the server), and then you change the components state, triggering a re-render that includes the div now.

@LinusBorg Thanks! I was just trying to understand why the error was being produced. Sounds like that's a solid reason.

I got this error due to "comments" containing angle braces outside one of my single-file-components.

e.g. for a file /my-other-button.vue

//
// README
//   - This component is an alternative to <my-button>
//
<template>
  <div>...</div>
</template>

<script>
...
</script>

I have this type of error in vuejs2 what can i do now ?
please help me i'm in trouble guyz

This is a closed issue about a different problem.

Use forum.vuejs.org for a questions, please.

Was this page helpful?
0 / 5 - 0 ratings