Vue-chartjs: Update infinite loop or no chart update

Created on 14 Apr 2020  ·  12Comments  ·  Source: apertureless/vue-chartjs

Expected Behavior

No error, and update chart.

Actual Behavior

Update chart, but an error:

vue.esm.js:628 [Vue warn]: You may have an infinite update loop in watcher with expression "chartData"

I have read #487 and #518 and #416 for try fix this, and #44 but without success.
If i juste use reactiveProps without update metohd, my chart dont update ;c

My actually chart code:

<script>
    import 'chartjs-adapter-moment';

    import { Line, mixins } from 'vue-chartjs'
    const { reactiveProp } = mixins;

    export default {
        extends: Line,
        mixins: [reactiveProp],
        props: ['options', 'newData'],
        mounted () {
            this.renderChart(this.chartData, this.options);
        },
        watch: {
            chartData: {
                deep: true,
                handler: function () {
                    this.renderChart(this.chartData, this.options);
                    //this.$data._chart.update();
                }
            }
        }
    }
</script>

And method to push data:

 var oldStas = instance.data_graphe;
                    oldStas.labels.push(new Date(Date.now()));
                    //On insére les valeur de la ram avec la date dans le tableau
                    oldStas.datasets[0].data.push({
                        x:  new Date(Date.now()),
                        y:memory
                    });

                    //On insére les valeur du cpu avec la date dans le tableau
                    oldStas.datasets[1].data.push({
                        x:  new Date(Date.now()),
                        y: cpu
                    });

                    if (oldStas.datasets[0].data.length > 30)
                        oldStas.datasets[0].data.splice(0, 1);

                    if (oldStas.datasets[1].data.length > 30)
                        oldStas.datasets[1].data.splice(0, 1);

                    instance.data_graphe = oldStas;

I have tried without oldStats variable, but no work.

Environment

  • vue.js version: 2.6.11
  • vue-chart.js version: 3.5.0
  • npm version: 6.14.4

For 2 days I have been looking for all possible solutions, but can't find any that work.

Thank you for making this vuejs "plugin" it is a very good plugins, and thank you for the help you would bring me! Sorry for my bad english ;c

❓ question

Most helpful comment

I had the same problem but kinda fixed it with with a custom watcher (without mixins) using a support boolean to execute the update only once per watch trigger. NB: the nextTick is the reason it works, and that's also a better solution than a setTimeout.

data: () => ({
    updating : false
}),

watch: {
    chartData: {
        deep: true,
        handler () {
            if (!this.updating && this.$data && this.$data._chart) {
                // Update the chart
                this.updating = true
                this.$data._chart.update()
                this.$nextTick(() => this.updating = false)
            }
        }
    }
}

I still need to test if better, but the infinite loop seems to be gone.

PS: I've also tried using lodash to compare newVal with oldVal without success, and was also time consuming

All 12 comments

Well, you have included the reactivePropMixin, which will create a variable named chartData and add a watcher to it.

But then, you add your own watcher to chartData with deep: true.
So the solution would be either to remove your watcher or the mixin.

If i remove the watcher, chart does't update.
And if i hower the chart, i have this error:

Chart.js:6719 Uncaught TypeError: Cannot read property 'skip' of undefined.

So i have to remove the mixins, but I still have the infi loop error.

My new code:

<script>
    import 'chartjs-adapter-moment';

    import { Line } from 'vue-chartjs'

    export default {
        extends: Line,
        props: ['options', 'chartData'],
        mounted () {
            this.renderChart(this.chartData, this.options);
        },
        watch: {
            chartData: {
                deep: true,
                handler: function () {
                    this.renderChart(this.chartData, this.options);
                    //this.$data._chart.update();
                }
            }
        }
    }
</script>

Thank for your help

This could be because of the deep: true.
Because this.chartData will get mutated by the chart.js core. And they have some references which will cause an infinite loop if you deep watch the whole object.

If you print out the object you will notice that it is quite big also.


Oh I see that you are using the x: y coordinate format. The provided mixin also does not support this format at the moment. Thats why the chart is not updating if you remove your own watcher.

Alright ! Thank for you reply.

I have tried to create a new properties "newData, but I have always the error :/

Could you explain to me how to settle it?

Whitout "deep: true", that don't work.

Thank.

Bump.

Getting the same error with the following:

import { Scatter } from 'vue-chartjs'

export default {
    extends: Scatter,
    props: ['chartData', 'options'],
    mounted () {
        this.renderChart(this.chartData, this.options)
    },
    watch: {
        chartData: {
            handler: function (newVal, oldVal) {
                if (newVal) {
                    this.$data._chart.destroy()
                    this.renderChart(this.chartData, this.options)
                }
            },
            deep: true
        }
    }
}

Bump.

is it possible to have a solution to this problem? In local dev mode activated, it does not affect the functioning of the site, but after build, the site makes a sort of Infinite loop and broken everything we are forced to reload the page.
Thanks for your reply.

Like I said, it is very unlikeley that it will work with deep: true as the chart.js object is very huge and have a circular structure.

And it also depends on how you mutate your data.

If you own watcher does not trigger if you just watch the chartData object, you can try to just watch for the datasets.


export default {

    watch: {
        'chartData.datasets': function(a, b) {}
    }
}

I just sucked, without the deep: true, nothing works. Maybe by calling a chartjs update just after pushing the data?

But how to do it, the renderChart function is not accessible from my other Vue Component :/

Have you tried to add the watcher to chartData.datasets instead of just chartData ?

I tried both. I also tried to update the chartjs from my VueComponent or is my chart but did not arrive

Edit:

In call this:

this.$children[0].$data._chart.update()

where is my chartjs is import, that work.

But I don't know if it's the right method !

I had the same problem but kinda fixed it with with a custom watcher (without mixins) using a support boolean to execute the update only once per watch trigger. NB: the nextTick is the reason it works, and that's also a better solution than a setTimeout.

data: () => ({
    updating : false
}),

watch: {
    chartData: {
        deep: true,
        handler () {
            if (!this.updating && this.$data && this.$data._chart) {
                // Update the chart
                this.updating = true
                this.$data._chart.update()
                this.$nextTick(() => this.updating = false)
            }
        }
    }
}

I still need to test if better, but the infinite loop seems to be gone.

PS: I've also tried using lodash to compare newVal with oldVal without success, and was also time consuming

@Hextar Thanks for your solution, it kinda works for me. Though, I'm still having a slow infinite loop (each ±100ms, my watcher is triggered).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sylvaincaillot picture sylvaincaillot  ·  3Comments

humanismusic picture humanismusic  ·  3Comments

kurbar picture kurbar  ·  4Comments

rzb picture rzb  ·  4Comments

syxolk picture syxolk  ·  5Comments