Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update infinite loop or no chart update #611

Closed
ph1823 opened this issue Apr 14, 2020 · 14 comments
Closed

Update infinite loop or no chart update #611

ph1823 opened this issue Apr 14, 2020 · 14 comments

Comments

@ph1823
Copy link

ph1823 commented Apr 14, 2020

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

@apertureless
Copy link
Owner

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.

@ph1823
Copy link
Author

ph1823 commented Apr 14, 2020

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

@apertureless
Copy link
Owner

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.

@ph1823
Copy link
Author

ph1823 commented Apr 15, 2020

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.

@btbritz
Copy link

btbritz commented Apr 16, 2020

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
        }
    }
}

@ph1823
Copy link
Author

ph1823 commented Apr 20, 2020

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.

@apertureless
Copy link
Owner

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) {}
	}
}

@ph1823
Copy link
Author

ph1823 commented Apr 20, 2020

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 :/

@apertureless
Copy link
Owner

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

@ph1823
Copy link
Author

ph1823 commented Apr 21, 2020

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 !

@Hextar
Copy link

Hextar commented Jun 15, 2020

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

@l-portet
Copy link

l-portet commented Jul 28, 2020

@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).

@hoonsungcho
Copy link

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) {}
	}
}

Removing mixins.reactiveProp and setting a deep watch to chartData for renderChart() worked for me. My infinite loop came from setting values for chartjs-annotation-plugin to the chart's options.

As @ph1823 mentioned, using the reactiveProp worked fine in local environment but browser became unresponsive in dev environment. I didn't find the inifinite loop warning until I removed the plugin code because this was a base component shared with other chart components.

I'm on vue 2.6.14 and vue-chartjs 3.5.1 and chartjs-plugin-annotation 0.5.7.

@shershen08
Copy link

to enforce the update of the chart w/o adding a watcher you could try adding a :key="chartData" attribute on a component itself, check here https://vuejs.org/api/built-in-special-attributes.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants