Vue js throws a maximum call stack error when updating a Chart component

I have successfully created a line chart using the Chart.js 3.5 library. The chart is responsive, all animations are working fine too. I am currently facing an issue where I am attempting to update the data from a parent component and trigger a chart update. However, upon calling chart.update(), an exception is thrown: "Uncaught (in promise) RangeError: Maximum call stack size exceeded". For this project, I am utilizing Vue 3 and Chart.js 3.5

Edit1: You can find a sandbox link to replicate this error.

The sequence of events is as follows:

  1. Parent passes chart data to child through props, with the shouldUpdate prop defaulting to false.
  2. Upon mounting the child component, the 'createChart' method draws the chart using data received from the parent.
  3. A Watcher in the child component monitors changes in the 'shouldUpdate' prop.
  4. Clicking a button in the parent triggers a change setting 'shouldUpdate' to True, which then activates the watcher in the child component.
  5. The child updates the chart data, refreshes the chart, and resets the 'shouldUpdate' prop back to False.

Expected outcome:

  1. Chart rendered with data from the parent component.
  2. Chart remains responsive and animated based on options provided by the parent.
  3. Chart updated when triggered by the parent.

Actual outcome:

  1. Chart displayed with data from the parent component as expected.
  2. Responsive and animated properties functioning properly according to parent settings.
  3. Chart update fails with a "Maximum call stack size exceeded" error message.

Key Parent element: Line.vue

<template>
    <raw
        :type="type"
        :options="options"
        :data="chartData"
        :shouldUpdate="shouldUpdate"
        :resetUpdate="resetUpdate"
    />
    <button @click="updateData"></button>
</template>

<script>
import Raw from "./Raw.vue";

export default {
    name: "App",
    components: {
        Raw,
    },
    computed: {
        chartData: function () {
            return this.data;
        },
    },

    methods: {
        updateData() {
            this.shouldUpdate = true;
        },
        resetUpdate() {
            this.shouldUpdate = false;
        },
    },
    data() {
        return {
            type: "line",
            shouldUpdate: false,
            options: {
                responsive: true,
                animation: true,
                maintainAspectRatio: false,
            },

            data: {
                labels: ["Jan", "Feb", "Mar", "Apr", "May", "June", "July"],
                datasets: [
                    {
                        label: "My line Dataset",
                        data: [65, 59, 80, 81, 56, 55, 40],
                        fill: false,
                        borderColor: "rgb(75, 192, 192)",
                        tension: 0.1,
                    },
                    {
                        label: "My second line Dataset",
                        data: [100, 79, 8, 80, 90, 55, 60],
                        fill: false,
                        borderColor: "rgb(75, 19, 192)",
                        tension: 0.1,
                    },
                ],
            },
        };
    },
};
</script>

Child component: Raw.vue

<template>
    <canvas />
</template>

<script>
import Chart from "chart.js/auto";

export default {
    name: "Raw",
    props: ["type", "data", "options", "shouldUpdate", "resetUpdate"],
    data() {
        return {
            chart: null,
            localData: null,
        };
    },
    computed: {
        parentEl: function () {
            const size = Math.min(this.$parent.width, this.$parent.height);
            return { height: size, width: size };
        },
    },
    watch: {
        shouldUpdate: function (val) {
            console.log(val);
            if (val) {
                console.log("updateTriggered");
                console.log(this.chart.data.datasets[0].data[5])
                this.chart.data.datasets[0].data[5] = Math.round(Math.random() * 100)
                console.log(this.chart.data.datasets[0].data[5])  // check if data changed
                this.chart.update(); // this seems to cause the error
            }
        },

    },
    methods: {
        createChart() {
            this.localData = this.data;
            this.chart = new Chart(this.$el, {
                type: this.type,
                data: this.localData,
                options: this.options,
            });
        },

    },

    mounted() {
        this.createChart();
    },
};
</script>

<style scoped>
</style>

Error traceback:

[Vue warn]: Unhandled error during execution of watcher callback 
  at <Raw type="line" options= {} data= {labels: Array(7), datasets: Array(2)}  ... > 
  at <App> 

runtime-core.esm-bundler.js?5c40:38 [Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next 
  at <Raw type="line" options= {} data= {labels: Array(7), datasets: Array(2)}  ... > 
  at <App> 
Uncaught (in promise) RangeError: Maximum call stack size exceeded
    at _resolveWithPrefixes (helpers.segment.js?dd3d:1797)
    at eval (helpers.segment.js?dd3d:1605)
    at _cached (helpers.segment.js?dd3d:1687)
    at Object.get (helpers.segment.js?dd3d:1604)
    at _resolveWithContext (helpers.segment.js?dd3d:1695)
    at eval (helpers.segment.js?dd3d:1647)
    at _cached (helpers.segment.js?dd3d:1687)
    at Object.get (helpers.segment.js?dd3d:1646)
    at toRaw (reactivity.esm-bundler.js?a1e9:743)
    at toRaw (reactivity.esm-bundler.js?a1e9:743)

sandbox reference for reproducing the error.

Answer №1

Expanding on Petru Tanas' workaround for deactivating reactivity in the chart, another option is to utilize a shallowRef

import { shallowRef } from 'vue';

data() {
    return {
      chart: null,
      localData: null,
    };
  },
methods: {
    createChart() {
      this.localData = this.data;
      this.chart = shallowRef(
        new Chart(this.$el, {
          type: this.type,
          data: this.localData,
          options: this.options,
        })
      );
    },
  },


Answer №2

In my specific scenario, I came up with a solution that may not be suitable for everyone.

The workaround involves making the chart object non-reactive to prevent Vue from tracking changes by moving it outside of the 'return' statement in the component's 'data'. Despite this change, the chart object remains accessible throughout the component and all its features (such as responsiveness and animations) continue to function properly. However, since Vue no longer tracks the object, issues may arise when attempting to pass it between components or utilize it with Vuex.

Here is the altered code:

Original Code:

<template>
    <canvas />
</template>

<script>
import Chart from "chart.js/auto";

export default {
    name: "Raw",
    props: ["type", "data", "options", "shouldUpdate", "resetUpdate"],
    data() {
        return {
            chart: null, // this line will change
            localData: null,
        };
    },
....
}

Modified Code:

<template>
    <canvas />
</template>

<script>
import Chart from "chart.js/auto";

export default {
    name: "Raw",
    props: ["type", "data", "options", "shouldUpdate", "resetUpdate"],
    data() {
        this.chart = null  // this line changed
        return {
            localData: null,
        };
    },
....
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Developing a custom feature with a ListView in nativescript-vue

Currently, I am immersing myself in the world of nativescript-vue and exploring ways to utilize single file components for enhancing the cleanliness of my code. To kickstart this endeavor, I initiated with a straightforward example that showcases beautiful ...

Revamping the hyperlinks in my collapsible menu extension

Is there a way to modify the links in this accordion drop menu so that they lead to external HTML pages instead of specific areas on the same page? I've tried various methods but can't seem to achieve it without affecting the styles. Currently, i ...

Error Message: Namespace Invalid in Node.js Application Utilizing Mongoose Library with MongoDB Atlas Database

I've been working on a Node.js application similar to Twitter, utilizing Mongoose (version 8.0.2) to communicate with a MongoDB Atlas database. However, I'm stuck dealing with an error message that reads MongoServerError: Invalid namespace specif ...

Trouble with the display:none attribute in Firefox and Chrome

<tr style="height:5px" id="TRRSHeaderTrialBar" name="TRRSHeaderTrialBar" style='display:none'> <tr id="TREmail" name="TREmail" style="height:1px;" nowrap style='display:none'> Using the code snippet above to hide the bar w ...

The Spring Boot application is having trouble retrieving static files

I started a fresh springboot project and organized the directory structure as follows: view image description here The yml configuration is outlined below: server: port: 8783 servlet: context-path: /spring-demo spring: scan : com.example appli ...

On the server side, the received Req.body appears as an empty object: { }

import { Injectable } from '@angular/core'; import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http'; import { Observable } from 'rxjs/Observable'; impo ...

Is it possible to remove certain 'css-' class names that MUI automatically applies to its components? (Using Next JS and MUI)

After successfully migrating my create-react-app project to Next JS using the latest version (12.1.0) and following the migration guide at https://nextjs.org/docs/migrating/from-create-react-app, I encountered an unexpected issue. Despite still using MUI a ...

Send a signal to a space, leveraging the distinct characteristics of each socket as input parameters

I am looking to send a function to a group of sockets, with each socket receiving the function along with a specific parameter unique to that particular socket. In my coding scenario, this distinctive variable represents the player's number. As socke ...

Create an HTML table row that includes a value along with its related sibling and parent values

I need to create an HTML table that compares segments in a JSON object. The format should display the segments along with their measures organized by domain group and vertical: ------------------------------------------------------------------------------ ...

Having issues with the addClass() method in JavaScript not functioning properly on hover

Coding Challenge <ul class="navBarExtended"> <li><a href="#">Link</a></li> </ul> Styling Solution .onHover{ text-decoration: underline; } Interactive Scripting $("ul.navBarExtended li a").hover( function() ...

Enhancing React URLs

Our company deals with URLs in this format: http://helloworld.com/product?filter[category][0]=persian We aim to transform the URL into a cleaner version: http://helloworld.com/product-persian When additional filters are added to the current UR ...

Unlinking styles from the template in Vue.js

I have a unique situation where my template contains a <style> block that needs to be positioned near its corresponding div due to CMS restrictions. However, when I try to integrate Vue.js into the mix, it appears to strip out the style block and di ...

Exploring the limitations of middlewares in supporting independent routers

When I examine the code provided, it consists of three distinct routers: const Express = require("express") const app = Express() // Three independent routers defined below const usersRouter = Express.Router() const productsRouter = Express.Router() cons ...

An elaborate warning mechanism in Redux-observable that does not trigger an action at the conclusion of an epic

I'm currently working on implementing a sophisticated alert system using redux and redux-observable. The requirements are: An action should request an alert: REQUEST_ALERT An action should create an alert and add an ID: SET_ALERT (handled in the ep ...

Refine your search with a JSON object description in expressJS and UnderscoreJS

[ { "id": 1, "description": "Empty the garbage bin", "completed": false }, { "id": 2, "description": "Dine out for dinner", "completed": false }, { "id": 3, "description": "Exercise at the fitness center", "com ...

The v-navigation-drawer component in Vuetify 2 is not reappearing after setting dialog to false

I am utilizing the navigation drawer to display my data list in a dialog box. Everything works fine when I initially open it, however, upon reopening the dialog, the entire navigation drawer vanishes. The data GET operation is functioning correctly with no ...

Is there a way to prevent an external script from making changes to an inline style?

There is a mysterious script running on a page that seems to be controlling the height of an inline style. The source of this script modifying the height property is unknown. <div class="vgca-iframe-wrapper wpfa-initialized" style="heigh ...

Converting JSON data into an HTML table

I'm struggling to convert a JSON object into an HTML table, but I can't seem to nail the format. DESIRED TABLE FORMAT: Last Year This Year Future Years 45423 36721 873409 CURRENT TABLE FORMAT: Last Year 45423 This ...

Using Vue 3 to dynamically render a component from a string

I have a question about the ValidateCheckboxes component I am working with. ValidateCheckboxes is a specialized list of checkboxes that I pass to the component as props. Here's how it typically looks: view image here view image here I use v-for to dis ...

Utilizing the Google Translate API within an ASP MVC framework to translate a div's content from English to Arabic

Currently, I am working on a small project that involves two divs: one for English and another for Arabic. Despite creating the project, I am encountering an issue with getting the translation from English to Arabic. Below is the code I have attempted, but ...