50

I cannot pass data from parent to child. I am using props, have tried returning data as well - no luck. I have a panel component (which is parent) with data and panelBody component (child)

Panel is as follows:

<template>
  <div id="panel">
    <div class="panel">
      <ul>
        <li v-for="shelf in shelfs">
          <panel-body :shelf="shelf" :selected.sync="selected"></panel-body>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import PanelBody from '../components/PanelBody'
export default {
  name: 'panel-body',
  components: {
    'panel-body': PanelBody
  },
  data: () => ({
    shelfs: [{
      name: 'shelf 1',
      books: [{
        title: 'Lorem ipum'
      }, {
        title: 'Dolor sit amet'
      }]
    }, {
      name: 'shelf 2',
      books: [{
        title: 'Ipsum lorem'
      }, {
        title: 'Amet sit dolor'
      }]
    }],
    selected: {}
  })
}
</script>

<style scoped>
a {
  color: #42b983;
}
</style>

My panelBody is:

<template>
  <div id="panel-body">
    <a href="#" v-on:click.prevent.stop="select">{{ shelf.name }}</a>
    <ul v-show="isSelected">
      <li v-for="book in shelf.books">{{ book.title }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'panel-body',
  props: ['shelf', 'selected'],
  computed: {
    isSelected: function () {
      return this.selected === this.shelf
    }
  },
  methods: {
    select: function () {
      this.selected = this.shelf
    }
  }
}
</script>

<style scoped>
a {
  color: #42b983;
}
</style>

Please help! Can't figure out the error "vue.esm.js?65d7:3877 Uncaught RangeError: Maximum call stack size exceeded". WHen I remove the data everything works like it should.

helvete
  • 2,455
  • 13
  • 33
  • 37
Teoman Kirac
  • 748
  • 3
  • 8
  • 16
  • 2
    You should not use `=>` functions with **data** property,, just use `data(){return{...}}` – Vamsi Krishna Jun 13 '17 at 11:39
  • 1
    Did it work...?? – Vamsi Krishna Jun 13 '17 at 14:40
  • no unfort, i get a "Property or method "shelfs" is not defined on the instance but referenced during render" and a "The "data" option should be a function that returns a per-instance value in component definitions." which is odd. – Teoman Kirac Jun 13 '17 at 15:20
  • Nuxt users: I encountered this error when introducing a new component in a template. As a quick workaround, I wrapped the component with `` and that alleviated the error, while I nailed down the ultimate culprit. – Kalnode Nov 15 '22 at 16:46

6 Answers6

114

The reason you have the error

Maximum call stack size exceeded

is because of this

import PanelBody from '../components/PanelBody'
export default {
  name: 'panel-body',
  components: {
    'panel-body': PanelBody
  },

You defined your Panel component with name: 'panel-body'. Change that to name: 'panel', and you will remove your circular reference.

The other issues mentioned in comments and the other answer generally apply as well. Here are working versions of your components.

Panel.vue

<template>
  <div id="panel">
    <div class="panel">
      <ul>
        <li v-for="shelf in shelfs">
          <panel-body :shelf="shelf" :key="shelf.name" :selected.sync="selected"></panel-body>
        </li>
      </ul>
    </div>
    {{selected}}
  </div>
</template>

<script>
import PanelBody from './PanelBody.vue'
export default {
  name: 'panel',
  components: {
    'panel-body': PanelBody
  },
  data(){
    return {
    shelfs: [{
      name: 'shelf 1',
      books: [{
        title: 'Lorem ipum'
      }, {
        title: 'Dolor sit amet'
      }]
    }, {
      name: 'shelf 2',
      books: [{
        title: 'Ipsum lorem'
      }, {
        title: 'Amet sit dolor'
      }]
    }],
    selected: {}

    }
  }
}
</script>

<style scoped>
a {
  color: #42b983;
}
</style>

PanelBody.vue

<template>
  <div id="panel-body">
    <a href="#" v-on:click.prevent.stop="select">{{ shelf.name }}</a>
    <ul v-show="isSelected">
      <li v-for="book in shelf.books">{{ book.title }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'panel-body',
  props: ['shelf', 'selected'],
  data(){
    return {
      internalSelected: null
    }
  },
  computed: {
    isSelected: function () {
      return this.internalSelected === this.shelf
    }
  },
  methods: {
    select: function () {
      this.internalSelected = this.shelf
      this.$emit("update:selected", this.internalSelected)
    }
  }
}
</script>

<style scoped>
a {
  color: #42b983;
}
</style>

I wanted to note one more thing. Because of this line in PanelBody, this.selected = this.shelf Vue will throw a warning that you are mutating a prop directly. Generally you should store a local copy of a property that you are going to mutate. I've updated the code above to do that.

Bert
  • 80,741
  • 17
  • 199
  • 164
  • 1
    @TeomanKirac you're welcome. There is one additional problem not currently covered; this code will produce a error that you are mutating a prop directly. I will post a fix for that in a minute. – Bert Jun 13 '17 at 16:02
  • @TeomanKirac I updated the PanelBody code with a quick explanation. – Bert Jun 13 '17 at 16:06
  • also see https://github.com/vuejs/vue/issues/9081 where I reported this to Vue – Emmanuel Mahuni Jan 16 '20 at 12:52
  • My issue was that my page name (`max-button`) was the same as the component name (`MaxButton`, which also works as `max-button`). – parker_codes Mar 25 '21 at 23:29
17

The name of the Vue you are in should not equal the name of the component you are importing.

In my case

<template>
 <div>
    <SingleStandard></SingleStandard>
 </div>
</template>

<script>
    import SingleStandard from '../components/SingleStandard';

    export default {
      name: 'SingleStandard',
      components: { SingleStandard },
    };
</script>

The above code was causing the same issue, but when i changed the name of the exported component it worked.

<template>
 <div>
    <SingleStandard></SingleStandard>
 </div>
</template>

<script>
    import SingleStandard from '../components/SingleStandard';

    export default {
      name: 'SingleStandardView', <-------------
      components: { SingleStandard },
    };
</script>

Please check your naming conventions for all future people :)

Sweet Chilly Philly
  • 3,014
  • 2
  • 27
  • 37
2

In my case, it happened when store overflow

const auth =  function({ next, store }){
    if(!store.getters.auth.loggedIn){
      return next('/login')
    }
    return next()
}
  
export {auth};

I updated like this and fixed up this issue. hope this would help you.

const auth =  function({ next, store }){
    if(store.getters.auth.loggedIn){
      return next('/login')
    }
    return next()
}
  
export {auth};
Jony Songa
  • 21
  • 2
2

Try to disable vue dev-tools extension...

I had this erratic error for months in my app, and it drove me crazy ! I just realized that it never happen again if I disable vue dev-tool chrome extension.

Not perfect but it could help.

Ousmane
  • 2,673
  • 3
  • 30
  • 37
  • 2
    I was seeing the call stack explosion only in certain situations; maybe vuex-related, maybe not. A browser restart worked for me; opening in a new tab didn't seem sufficient. And of course not until after I'd upgraded npm, walked back and forth along the commit history a couple of times, and tried it on a separate machine. – Robin Macharg Feb 11 '22 at 12:43
1

the sync has changed in vue2.

https://v2.vuejs.org/v2/guide/components.html#sync-Modifier

you should use it this way

<panel-body :shelf="shelf" :selected="selected" @update:selected="val => selected = val"></panel-body>

and on child

this.$emit('update:selected', shelf)

OR you could do just this

<panel-body :shelf="shelf" :selected="shelf == selected" @select="selected = shelf"></panel-body>

and on child

this.$emit("select")
tony19
  • 125,647
  • 18
  • 229
  • 307
John
  • 1,081
  • 1
  • 9
  • 34
  • 1
    You're right, but not quite. You can use `prop.sync="parentProp"` as in the question, it AUTO expands to prop-event declarations. The only thing should be changed in the question code: instead of manually mutating props `this.selected = this.shelf` there should be `this.$emit('update:selected', this.shelf)`. Example: https://jsfiddle.net/wostex/63t082p2/72/ – Egor Stambakio Jun 13 '17 at 12:22
  • have tried all variations of what John and Wostex have suggested with no luck. The error now is "Property or method "shelfs" is not defined on the instance but referenced during render." But this error is only different cause the data is not a function now, like Vamsi Krishna suggested.. – Teoman Kirac Jun 13 '17 at 15:14
  • the data must be a function data: function(){ return {bla: true} } – John Jun 13 '17 at 17:16
0

I was doing something like this:

  watch: {
    topics() {
      this.topics = this.topics.slice(0, 40);
    },
  },

Each time, it creates a new array with new reference, that should be done with splice:

 topics() {
    this.topics.splice(40);
 },
Amir2mi
  • 896
  • 9
  • 13