Search code examples
javascriptvue.js

Vue.js "Maximum call stack size exceeded" error. Passing data from parent to child failing


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.


Solution

  • 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.