Adding Bar Chart to a React Component - javascript

I've been trying to add a Bar Chart to a React Component and used this youtube tutorial for it. I use react-chartjs-2
My component looks like this:
import React, { useEffect, useState } from "react";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const Daily = () => {
const [chartData, setChartData] = useState({
datasets: [],
})
const [chartOptions, setChartOptions] = useState({
datasets: [],
})
useEffect(() => {
setChartData({
labels: [1,2,3,4,5,6,7],
datasets: [
{
label: 'e',
data: [2,6,3,8,4,6,77],
}
]
});
setChartOptions({
responsive: true,
plugins : {
legend: {
position: "top",
},
title: {
display: true,
text: 'eh'
}
}
})
}, [])
return (
<div className="Daily">
<Bar data={chartData} options={chartOptions} />
</div>
);
};
export default Daily;
Even though I followed the tutorial exactly I get a lot of errors in the console and no Chart shows up. The errors are:
Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Uncaught TypeError: Cannot read properties of null (reading 'useRef')
react-dom.development.js:18687 The above error occurred in the
<ForwardRef(ChartComponent)> component:
My React and React DOM versions are
"react": "^18.2.0",
"react-chartjs-2": "^4.2.0",
"react-dom": "^18.2.0", so they do seem to be matching and I'm wondering why I'm getting the error. useRef is also not referenced anywhere.
Did anyone have a similiar problem?

I think chartOptions should be {} and not {datasets:[]}
const [chartOptions, setChartOptions] = useState({});

Related

What is instance for update chartjs

I'm trying to make reactive changed graph, so I change some value, then my computed ChartData() put it to component, but nothing works. I'm trying to use update() function, but I have no instance for it.
How can I use function as .update(), .destroy() for my LineChart when I have no instance for it? Can I get instance of graph? What's wrong?
I guess i have 2 methods for update it? Is it?
Update:
Actually I get instance, but problem still exist. Computed property doesn't update data in Line and update() does nothing.
<template>
<div class="graph__busManag">
<Line ref="Graph" id="Chart" :data="ChartData" :options="options" :plugins="plugins" />
</div>
</template>
<script>
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from "chart.js";
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
import { Line } from "vue-chartjs";
data() {
return {
data: ...something,
option: ...somedata,
},
computed: {
ChartData() {
return this.data; // Should update graph, but doesn't , despite the fact that it is constantly triggered
}
},
mounted() {
this.data.datasets.data = [50, 60, 70, 80, 90, 100];
this.data = {...this.data};
const chartInstance = this.$refs.Graph.chart
console.log(chartInstance);
chartInstance.update(); // not working
chartInstance.reset(); // not w
},
}
You can use the static method getChart to which you pass the id you have given your chart. Here you get the chart instance back to which you can call update:
import { Chart } from 'chart.js';
const chartInstance = Chart.getChart(ownChartId);

vue-chartjs vue 2 add custom chart

I'm trying to add a custom chart in a vue2 app.
The new component is registered but the extended functions have no effect.
I follow some guides: https://vue-chartjs.org/guide/#custom-new-charts, https://www.chartjs.org/docs/latest/developers/charts.html, search in issues and try many combinations but no result.
I am miss something ?
Thank you 🙏
The code :
<template>
...
<Cluster
:chart-options="chartOptions"
:chart-data="chartData"
:width="700"
:height="550"
:plugins="plugins"
ref="graph"
/>
...
</template>
import {Chart as ChartJS, Title, Tooltip, Legend, PointElement, LinearScale, BubbleController} from 'chart.js'
import ChartDataLabels from "chartjs-plugin-datalabels";
import {generateChart} from "vue-chartjs/legacy";
class ClusterController extends BubbleController {
initialize() {
console.log('foo');
}
draw() {
console.log('bar');
}
}
// ClusterController.id = 'cluster-chart';
// ClusterController.defaults = BubbleController.defaults;
const Cluster = generateChart("cluster-chart", "bubble", ClusterController);
ChartJS.register(Title, Tooltip, Legend, PointElement, LinearScale, ChartDataLabels)
// ChartJS.register(Title, Tooltip, Legend, PointElement, LinearScale, ChartDataLabels, ClusterBubbleController)
export default {
name: "ProjectRiskManagementGraphTab",
components: {
Cluster,
},
computed: {
chartData() {
const data = ...;
return {
datasets: [
{
data: data,
}
]
};
},
},
data() {
return {
chartOptions: {
responsive: true,
maintainAspectRatio: true,
},
}
}
};
</script>

How to update a chart on prop change in vue-chartjs

First, I would like to emphasize that I am a completely new to Vue (and webdev in general).
I have to make a UI where I have to show some data that I fetch from an API. But I have some issues with chart.js: I can't update my chart when prop change.
Currently, I have the following setup:
Card.vue:
<script>
import DoughnutChart from './DoughnutChart.js'
export default {
name: 'Card',
components: {
DoughnutChart
},
props: ['scheda'],
created() {
console.log(this.scheda)
}
}
</script>
<template>
<DoughnutChart type="abcd" :chartData="this.scheda"/>
</template>
DoughnutChart.js:
import { defineComponent, h } from 'vue'
import { Doughnut } from 'vue-chartjs'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
ArcElement,
CategoryScale
} from 'chart.js'
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
export default defineComponent({
name: 'DoughnutChart',
components: {
Doughnut
},
props: ['type', 'chartData'],
setup(props) {
const chartData = {
labels: ['A', 'B'],
datasets: [
{
backgroundColor: ["#ff3333", "#131b2e"],
cutout: "75%",
borderWidth: 0,
data: [props.chartData.value, (100 - props.chartData.value)]
}
]
}
const chartOptions = {
plugins: {
legend: {
display: false
}
},
responsive: true,
maintainAspectRatio: false
}
return () =>
h(Doughnut, {
chartData,
chartOptions
})
}
})
this.scheda is a reactive value and when it changes the chart should update accordingly.
I have read the documentation, but I simply can't wrap my head around it.
I have also tried searching on the internet, but all examples reference the older version of the chart library.
Can someone help me with this issue? Thanks!
If you want to create a chart js and integrate with view, you can create your canvas on js outside of vue js flow, and then get the element reference inside vue and then integrate it, the big problem rendering components inside vue is that you need update your charts, it may cause unexpected behavior on canvas. Because tools like Vue are designed to refresh the html and canvas always are a single html tag with a lot of interactivity in js(not much html).
Vue-chart
You can install
vue-chart.js
It supports data updates and also you can access to the chart ref.
Updating charts: https://vue-chartjs.org/guide/#updating-charts
Acesing to ref: https://vue-chartjs.org/guide/#access-to-chart-instance

Reading map and ForwardRef error in Chart.JS - Javascript

I am attempting to create charts with chart.js. I have followed the example code on the chart.js website and have been able to get the example code to work. I have tried amending the code to process the data for the chart externally from the javascript file creating the chart, then providing the data through the chart parameters. Doing this, I get the following console error:
I have the following data in my data.js file:
export const UserData = [
{
id: 1,
year: 2018,
users: 2000,
},
{
id: 2,
year: 2019,
users: 2500,
},
{
id: 3,
year: 2020,
users: 3000,
}
]
I have the following code in my App.js file:
import React from "react";
import { UserData } from './Components/data';
import { Graph } from "./Components/graph";
const data = {
labels: UserData.map((data) => data.year),
datasets: [{
label: 'Revenue',
data: UserData.map((data) => data.users),
backgroundColor: ['#CCD4BF'],
borderColor: [],
borderWidth: 1
}]
};
function App() {
return <Graph graphData={data}/>
}
I have the following code in my graph.js file:
import React from 'react';
import { Bar } from 'react-chartjs-2';
import {Chart as ChartJS,} from 'chart.js/auto'
export function Graph(graphData) {
return <Bar data={graphData}/>
}
Is there anything that I am missing in my code to resolve the "reading map" and "ForwardRef" errors?
I had the same issue, to solve this read about migration to react-chartjs-2#4 here, that helped for me, I used the tree-shakable approach:
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, LineController, LineElement, PointElement, LinearScale, Title } from 'chart.js';
ChartJS.register(LineController, LineElement, PointElement, LinearScale, Title);
<Chart type='line' data={chartData} />

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

Categories