Skip to content
Advertisement

Reactive chart with vue-chartjs throws “Maximum call stack size exceeded”

I tried to build a reactive chart with vue-chartjs (Vue v3, Vue ChartJS v4, ChartJS v3). But instead of updating the data in the diagram it just throws the following stack trace every time the interval functions for data computation gets executed:

Uncaught RangeError: Maximum call stack size exceeded
    at Object.get2 [as get] (reactivity.esm-bundler.js:432:12)
    at toRaw (reactivity.esm-bundler.js:953:29)
    at instrumentations.<computed> (reactivity.esm-bundler.js:424:25)
    at Proxy.value (helpers.segment.js:1583:26)
    at instrumentations.<computed> (reactivity.esm-bundler.js:424:42)
    at Proxy.value (helpers.segment.js:1583:26)
    at instrumentations.<computed> (reactivity.esm-bundler.js:424:42)
    at Proxy.value (helpers.segment.js:1583:26)
    at instrumentations.<computed> (reactivity.esm-bundler.js:424:42)
    at Proxy.value (helpers.segment.js:1583:26)

I have no clue where to look for the error. I understand that it hints maybe a recursion but I have no idea where this recursion could happen.

If I use static dummy data instead of computing an array with data it displays that data just fine in the diagram.

It’s basically the same problem as in this question here: Why using ref() in Vue3 causes “RangeError: Maximum call stack size exceeded”?

I attached below the code of the two components that lead to this error message.

LineChart.vue

<script setup lang="ts">
import { Line } from 'vue-chartjs'
import {
  Chart as ChartJS,
  Title,
  Tooltip,
  Legend,
  LineElement,
  LinearScale,
  PointElement,
  CategoryScale,
  Chart
} from 'chart.js'
import type {ChartOptions,ChartData} from 'chart.js'

interface Props {
  chartData: ChartData
  chartOptions: ChartOptions
}

const props = defineProps<Props>();

ChartJS.register(
    Title,
    Tooltip,
    Legend,
    LineElement,
    LinearScale,
    PointElement,
    CategoryScale
)

const plugins = [];
</script>

<template>
  <Line
      chartId="line-chart"
      :width="400"
      :height="200"
      :chart-data="this.props.chartData"
      :chartOptions="this.props.chartOptions"
      :plugins="this.plugins"
  />
</template>

DistanceWidget.vue

<script setup lang="ts">
import {computed, reactive} from "vue";
import type DeviceDistance from "../../../model/DeviceDistance.js";
import LineChart from "../../chart/LineChart.vue";
import type {ChartData} from "chart.js";

interface Props {
  device: DeviceDistance
}

const props = defineProps<Props>();
const device = reactive<DeviceDistance>(props.device);
const currentDistance = computed<number>((): number => {
  const tmpDistance = device.data?.distance;
  return tmpDistance && tmpDistance < 255 ? tmpDistance : 183;
});

const chartLabels = reactive<string[]>([]);
const chartValues = reactive<number[]>([]);

setInterval(() => {
  // Make sure we always have no more than 10 data points
  if (!currentDistance) return;

  chartLabels.push(new Date().toString())
  chartValues.push(currentDistance.value)

  if (chartValues.length > 10) {
    chartLabels.shift();
    chartValues.shift();
  }
}, 500)

const chartDataComp = computed<ChartData>((): ChartData => {
    return {
      labels: chartLabels,
      datasets: [
        {
          label: 'Distance',
          backgroundColor: '#f87979',
          tension: 0.5,
          data: chartValues
        }
      ]
    }
});

const chartOptions = {
  responsive: true,
  maintainAspectRatio: false,
}
</script>

<template>
  <LineChart :chartData="chartDataComp" :chartOptions="chartOptions" />
</template>

Advertisement

Answer

I found a working example in the documentation: https://codesandbox.io/s/github/apertureless/vue-chartjs/tree/main/sandboxes/reactive-prop

It’s rather complex and not very intuitive. I would have never figured it out myself. But if you build up on it, you get it working. The spread operators (...) are key to get out of the Maximum call stack size exceeded hell.

Good luck!

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement