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!