Search code examples
vue.jsvue-componentvue-chartjs

Got "Maximum call stack size exceeded" with Vue using ChartJS


I want to pass the data from Parent to GrandChild (which is Chart) with reactivity so I'm using props.

This is the Parent

The "kalkulator-data" component below is worked and it's used to adding data to Parent.

<template>
    <div class="container">
        <div class="row">

                <kalkulator-data 
                    :saldoAwal="saldoAwal" 
                    :saldoTerpakai="saldoTerpakai" 
                    :saldoAkhir="saldoAkhir"
                    :dataKategori="dataKategori"
                    :dataPengeluaran="dataPengeluaran"

                    @inputSaldo="saldoAwal = $event"
                    @inputPengeluaran="dataPengeluaran.push($event)">

                </kalkulator-data>

                <kalkulator-chart
                    :chartData="chartData"
                    :chartOptions="chartOptions">    

                </kalkulator-chart>

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

<script>
    import Chart from './Bagian/Chart.vue';
    import Data from './Bagian/Data.vue';

    export default {
        data(){
            return {
                saldoAwal : 0,
                dataPengeluaran : [{ kategori : 'Abi', nominal : 12}, { kategori : 'Ilham', nominal : 13}],
                dataKategori : [
                    'Makan dan Minum',
                    'Berbelanja',
                    'Perlengkapan Rumah',
                    'Transportasi',
                    'Kendaraan',
                    'Gaya Hidup dan Hiburan',
                    'Komunikasi',
                    'Pengeluaran dan Finansial',
                    'Investasi',
                ],
                chartOptions : {responsive: true, maintainAspectRatio: false}
            }
        },
        computed : {
            saldoTerpakai(){
                var saldoPakai = 0;
                for(var data of this.dataPengeluaran){
                    saldoPakai += parseInt(data.nominal);
                }
                return saldoPakai;
            },
            saldoAkhir(){
                return this.saldoAwal - this.saldoTerpakai;
            },
            getRandomColor(){
                var letters = '0123456789ABCDEF';
                var color = '#';
                for (var i = 0; i < 6; i++) {
                    color += letters[Math.floor(Math.random() * 16)];
                }
                return color;
            },
            chartLabels(){
                var chartLabels = [];
                for (var data of this.dataPengeluaran ){
                    chartLabels.push(data.kategori);
                    // backgroundColor.push(this.getRandomColor);
                };
                return chartLabels;
            },
            chartId(){
                var chartData = [];
                for (var data of this.dataPengeluaran ){
                    chartData.push(parseInt(data.nominal));
                    // backgroundColor.push(this.getRandomColor);
                };
                return chartData;
            },
            chartData(){
                return {
                    labels: this.chartLabels,
                    datasets: [
                      {
                        label: 'Data One',
                        backgroundColor: '#f87979',
                        data: this.chartId
                      }
                    ]
                }
            }
        },
        methods : {

        },
        components : {
            'kalkulator-data' : Data,
            'kalkulator-chart' : Chart
        },
    }
</script>

This is the Child

<template>
    <div class="col-sm-6">
        <div class="row">
            <h1 class="text-center">Struktur Pengeluaran</h1>

            <chart-comp 
                :chartData="chartData"
                :chartOptions="chartOptions">               
            </chart-comp>

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

<script>
    import chartComp from './Chart-Data.js'

    export default{
        props : ['chartData', 'chartOptions'],
        components : {
            'chart-comp' : chartComp
        }
    }
</script>

And this the GrandChild which is Chart

import {Bar} from 'vue-chartjs'

export default Bar.extend({
    props : ['chartData', 'chartOptions'],
    computed : {
        renderChart(){
            this.renderChart(this.chartData, this.chartOptions);
        }
    },
    mounted () {
        this.renderChart;
    }
})

I'm using computed to keep the reactivity when I'm added data, but It said in console :

app.js:5366 [Vue warn]: Error in mounted hook: "RangeError: Maximum call stack size exceeded"

found in

---> <ChartComp>
       <KalkulatorChart> at C:\xampp\htdocs\SemuaOkee-Laravel\resources\assets\js\components\Bagian\Chart.vue
         <Kalkulator> at C:\xampp\htdocs\SemuaOkee-Laravel\resources\assets\js\components\Kalkulator.vue
           <Root>
warn @ app.js:5366
handleError @ app.js:5451
callHook @ app.js:7490
insert @ app.js:8315
invokeInsertHook @ app.js:10143
patch @ app.js:10308
Vue._update @ app.js:7248
updateComponent @ app.js:7371
get @ app.js:7710
Watcher @ app.js:7693
mountComponent @ app.js:7375
Vue$3.$mount @ app.js:12503
Vue$3.$mount @ app.js:14602
Vue._init @ app.js:8942
Vue$3 @ app.js:9027
(anonymous) @ app.js:29926
__webpack_require__ @ app.js:20
(anonymous) @ app.js:29888
__webpack_require__ @ app.js:20
(anonymous) @ app.js:63
(anonymous) @ app.js:66
app.js:5455 RangeError: Maximum call stack size exceeded
    at Watcher.evaluate (app.js:7809)
    at VueComponent.computedGetter [as renderChart] (app.js:8064)
    at VueComponent.renderChart (app.js:61047)
    at Watcher.get (app.js:7710)
    at Watcher.evaluate (app.js:7810)
    at VueComponent.computedGetter [as renderChart] (app.js:8064)
    at VueComponent.renderChart (app.js:61047)
    at Watcher.get (app.js:7710)
    at Watcher.evaluate (app.js:7810)
    at VueComponent.computedGetter [as renderChart] (app.js:8064)

When I'm still not added the data, it's work perfectly ( that's why I'm hardcoded two objects in Parent to test my Chart in the beginning), but when I'm added a data it's got an error.

In other words, It's work on static but not with dynamic data.


Solution

  • In your grandchild component, you have defined a computed property renderChart which returns this.renderChart. This is what is causing the Maximum call stack size error.

    You should just call the renderChart method in the mounted hook:

    import { Bar } from 'vue-chartjs'
    
    export default Bar.extend({
        props: ['chartData', 'chartOptions'],
        mounted() {
            this.renderChart(this.chartData, this.chartOptions);
        }
    })
    

    If you need the chart data to be reactive, vue-chartjs provides an admittedly goofy way to specify that using a mixin:

    import { Bar, mixins } from 'vue-chartjs'
    
    export default Bar.extend({
        props: ['chartData', 'chartOptions'],
        mixins: [mixins.reactiveProp],
        mounted() {
            this.renderChart(this.chartData, this.chartOptions);
        }
    })
    

    Here's the vue-chartjs documentation on using reactive data.