Uncaught ReferenceError: Cannot access 'dataState' before initialization - reactjs

I have a question regarding useState.
When I create a usestate like this =>
const [dataChart, setDataChart] = useState(dataState());
Suddenly an error message appears in the console
(Uncaught ReferenceError: Cannot access 'dataState' before initialization)
Why is that about and what solution can you give me? Thank you
MyCode =
const [filterdata, setFilterdata] = useState([]);
const [query, setQuery] = useState("");
const [dataProvinsi, setDataProvinsi] = useState([]);
const [dataKota, setDataKota] = useState([]);
const [dataKecamatan, setDataKecamatan] = useState([]);
const [dataKelurahan, setDataKelurahan] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isLoadingKota, setIsLoadingKota] = useState(false);
const [isLoadingKecamatan, setIsLoadingKecamatan] = useState(false);
const [selectedValue, setSelectedValue] = useState("");
const [dataChart, setDataChart] = useState(dataState());
const provinsiRef = useRef([]);
const kotaRef = useRef([]);
const kecamatanRef = useRef([]);
const getDataAllProvinsi = () => {
setIsLoading(true);
getStreetDallProvinsi()
.then((resolve) => {
setDataProvinsi(resolve);
setFilterdata(resolve);
console.log(resolve);
})
.catch((reject) => {
console.log(reject);
})
.finally(setIsLoading(false));
};
const handlesearch = (event) => {
const getSearch = event.target.value;
if (getSearch.length > 0) {
const searchdata = dataProvinsi.filter((item) =>
item.provinsi.toLowerCase().includes(event.target.value.toLowerCase())
);
setDataProvinsi(searchdata);
} else {
setDataProvinsi(filterdata);
}
setQuery(getSearch);
};
const handleProvinsi = async (index) => {
try {
const provinsi = provinsiRef.current[index].dataset.prov;
setIsLoading(true);
const result = await getStreetallKota(provinsi);
setDataKota(result);
console.log(result);
} catch (error) {
console.log("salah");
} finally {
setIsLoading(false);
}
};
const handleKota = async (provinsi, index) => {
try {
const kota = kotaRef.current[index].dataset.city;
setIsLoadingKota(true);
const result = await getStreetallKecamatan(provinsi, kota);
setDataKecamatan(result);
console.log(result);
} catch (error) {
console.log("salah");
} finally {
setIsLoadingKota(false);
}
};
const handleKecamatan = async (provinsi, kota, index) => {
try {
const kecamatan = kecamatanRef.current[index].dataset.camat;
setIsLoadingKecamatan(true);
const result = await getStreetallKelurahan(provinsi, kota, kecamatan);
setDataKelurahan(result);
console.log(result);
} catch (error) {
console.log("salah");
console.log(error);
} finally {
setIsLoadingKecamatan(false);
}
};
useEffect(() => {
getDataAllProvinsi();
}, []);
const colorCode = "#0066FF";
const colorFont = "#8E9093";
const dataState = () => ({
data: {
dataProv: {
labels: dataProvinsi.map((o) => o.provinsi),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataProvinsi.map((o) => o.total_street),
},
],
},
dataKota: {
labels: dataKota.map((o) => o.kota),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataKota.map((o) => o.total_street),
},
],
},
dataKecamatan: {
labels: dataKecamatan.map((o) => o.kecamatan),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataKecamatan.map((o) => o.total_street),
},
],
},
},
options: {
plugins: {
legend: {
display: false,
labels: {
font: {
color: colorFont,
},
},
},
},
scales: {
x: {
grid: {
display: false,
},
beginAtZero: false,
ticks: {
color: colorFont,
},
},
y: {
grid: {
display: false,
},
beginAtZero: true,
ticks: {
color: colorFont,
},
},
},
},
});
const plugins = [
{
beforeDraw: function (chart) {
if (chart.chartArea) {
let ctx = chart.ctx;
let chartArea = chart.chartArea;
let barArray = chart.getDatasetMeta(0).data;
ctx.fillStyle = "#B2D1FF85";
for (let i = 0; i < barArray.length; i++) {
const { x, width } = barArray[i];
ctx.fillRect(
x - width / 2,
chartArea.top,
width,
chartArea.bottom - chartArea.top
);
}
}
},
},
];
useEffect(() => {
setDataChart(dataState());
}, [selectedValue]);

You're making a call to dataState() method in your initialization well before it's defined (notice how dataState is declared something like 50 lines below where you're calling it). dataState as an undefined variable can't be invoked. Either put the definition for dataState before your useState invocation, use a better initial state for dataChart, or as it appears to me, dataChart can be derived from your other state variables and thus should not be a separate state.

the issue is in the error message itself, 'Uncaught ReferenceError: Cannot access 'dataState' before initialization'. you are trying to use dataState function before its declaration. quick solution is you can get the dataState function to outside of the component itself and declare it above the file.
and to use it as the state, your function should return something out of it also. and here you've violated the state management also. I would suggest you move the dataState function inside a useMemo hook. like this, if it's calculating anything using any state variables from props, otherwise I don't see a point to assign those values to another state also.
const dataChart = useMemo(() => ({
data: {
dataProv: {
labels: dataProvinsi.map((o) => o.provinsi),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataProvinsi.map((o) => o.total_street),
},
],
},
dataKota: {
labels: dataKota.map((o) => o.kota),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataKota.map((o) => o.total_street),
},
],
},
dataKecamatan: {
labels: dataKecamatan.map((o) => o.kecamatan),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataKecamatan.map((o) => o.total_street),
},
],
},
},
options: {
plugins: {
legend: {
display: false,
labels: {
font: {
color: colorFont,
},
},
},
},
scales: {
x: {
grid: {
display: false,
},
beginAtZero: false,
ticks: {
color: colorFont,
},
},
y: {
grid: {
display: false,
},
beginAtZero: true,
ticks: {
color: colorFont,
},
},
},
},
}), [])

You should return something in the dataState to assign it to a useState.
useState is used to provide some value to a state so it must receive some value from the dataState function.

Related

next chart.js how to change date display

how to change date display on my chart? Here below my chart:
How to change date displaying way? Is there any way to display it horizontaly and f.e. not every date, but every third?
Here below my code:
import { ChartProps } from "./Chart.props";
import styles from "./Chart.module.css";
import React, { useState, useEffect, useRef } from 'react';
import { Button } from "../Button/Button";
import { options } from "./ChartConfig.js";
import { Chart as ChartJS, ArcElement, CategoryScale, LinearScale, PointElement, LineElement, Filler, Tooltip, Legend, ScriptableContext, } from "chart.js";
import { Chart as ReactChart, Line } from "react-chartjs-2";
import coinGecko from "../../../apis/coinGecko";
ChartJS.register(ArcElement, Tooltip, Filler, Legend, CategoryScale, LinearScale, PointElement, LineElement);
export const CoinPriceChart = (props) => {
const [timeFormat, setTimeFormat] = useState("30d");
const [interval, setInterval] = useState("hourly");
const [coinPrice, setCoinPrice] = useState([]);
const [coinHistory, setCoinHistory] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const x = [];
const y = [];
const determineTimeFormat = () => {
switch (timeFormat) {
case "1":
setInterval("5min");
setTimeFormat("1");
case "7":
setInterval("hourly");
setTimeFormat("7");
case "30":
setInterval("daily");
setTimeFormat("30");
default:
setInterval("hourly");
}
};
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
await fetch(`https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=${timeFormat}&interval=${interval}`)
.then((response) => {
return response.json();
})
.then((data) => {
for (let i = 0; i < data.prices.length; i++) {
x.push(data.prices[i][1])
setCoinPrice(x)
}
for (let i = 0; i < data.prices.length; i++) {
y.push(new Date(data.prices[i][0]).toLocaleDateString("en-GB"))
setCoinHistory(y)
}
})
setIsLoading(false);
};
fetchData();
}, [timeFormat]);
const chart = {
labels: coinHistory,
datasets: [
{
data: coinPrice,
pointRadius: 0,
pointHoverRadius: 2,
backgroundColor: (context: ScriptableContext<"line">) => {
const ctx = context.chart.ctx;
const gradient = ctx.createLinearGradient(0, 0, 0, 330);
gradient.addColorStop(0, "rgba(91,56,237,0.45)");
gradient.addColorStop(1, "rgba(91,56,237,0.0)");
return gradient;
},
borderColor: "rgba(91,56,237,255)",
fill: true,
}
],
options: {
...options
},
};
console.log(timeFormat)
return (
<div
className={styles.Chart}
{...props}
>
<Line id="myChart" data={chart} options={options} />
<div className="chart-button mt-1">
<Button
onClick={() => setTimeFormat("1")}
appearance={"primary"} >
24h
</Button>
<Button
onClick={() => setTimeFormat("7")}
appearance={"primary"}
>
7d
</Button>
<Button
onClick={() => setTimeFormat("30")}
appearance={"primary"}
>
30d
</Button>
</div>
</div>
);
};
I've been searching for it on a doc., but I haven't found this info.
And here below my options code:
export const options = {
plugins: {
// show legends for our graph
legend: {
display: false,
},
},
lineHeightAnnotation: {
always: true,
lineWeight: 3,
},
// animate in
animation: {
duration: 1,
},
// show the x and y scales
scales: {
x: {
display: true,
},
y: { display: true },
},
time: {
useUTC: true
},
xAxis: {
type: 'datetime'
},
};
Thanks in advance!:)
Okay here it is my ChartConfig.js
export const options = {
plugins: {
// show legends for our graph
legend: {
display: false,
},
},
lineHeightAnnotation: {
always: true,
lineWeight: 3,
},
// animate in
animation: {
duration: 1,
},
// show the x and y scales
scales: {
x: {
display: true,
ticks: {
maxRotation: 0, // this line to make labels horizontal
maxTicksLimit: 5, // this is num of labels limit
}
},
y: {
display: true
},
xAxes: [{
ticks: {
maxRotation: 90
}
}]
},
time: {
useUTC: true
},
};
And as a result:

ChartJS - Why is the transition on the first dataset bad?

So I have react-datepicker to choose the dates of my data, the data that is returned looks like this:
{clicks: 6, date: "2022-05-15", link: "instagram"},
{clicks: 5, date: "2022-05-15", link: "google"}...
On load and every change in the datePicker, I have this function which sets the data variables, the date will be moment():
const onChangeClick = (dates: any) => {
const [start, end] = dates;
setStartDateClick(start);
setEndDateClick(end);
if (start & end) {
let dates = {
dateFrom: moment(start).format('YYYY-MM-DD'),
dateTo: moment(end).format("YYYY-MM-DD")
}
_statisticsService.getClickStats(dates).then((response: any) => {
response.data.map((e: any) => {
clicksData[e?.link].push(e)
})
setInstaDataClick(clicksData['instagram'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setFacebookDataClick(clicksData['facebook'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setGoogleDataClick(clicksData['google'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setTripadvisorDataClick(clicksData['tripadvisor'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
setWebsiteDataClick(clicksData['website'].map((e: any) => {
return { clicks: e.clicks, date: moment(e.date) }
}))
})
setLabelsClick(getDaysArray(clicksData[0]?.date, clicksData[clicksData.length - 1].date))
}
};
And later I use this values in the datasets:
const data = {
labels: labelsClick,
datasets: [
{
label: 'Instagram',
data: instaDataClick,
borderColor: '#c32aa3',
backgroundColor: '#c32aa3',
},
{
label: 'Facebook',
data: facebookDataClick,
borderColor: '#1877f2',
backgroundColor: '#1877f2',
},
{
label: 'Google',
data: googleDataClick,
borderColor: '#ea4335',
backgroundColor: '#ea4335',
},
{
label: 'Tripadvisor',
data: tripadvisorDataClick,
borderColor: '#34e0a1',
backgroundColor: '#34e0a1',
},
{
label: 'Website',
data: websiteDataClick,
borderColor: '#010101',
backgroundColor: '#010101',
},
],
};
And at the end this is how I preview the Line chart
<div className='datepicker-container'>
<DatePicker
onChange={onChangeClick}
startDate={startDateClick}
endDate={endDateClick}
selectsRange
placeholderText='Select Date'
isClearable
/>
</div>
<div className='chart'>
<Line options={options} data={data} />
</div>
The options for this line look like this, x axis is the date and y is the clicks:
const options: ChartOptions<'line'> = {
responsive: true,
scales: {
y: {
title: {
display: true,
text: 'Date'
},
ticks: {
precision: 0,
}
},
x: {
type: 'time',
title: {
display: true,
text: "Date"
},
time: {
unit: 'day',
stepSize: 1,
displayFormats: {
day: "MMM DD"
}
}
},
},
parsing: {
xAxisKey: 'date',
yAxisKey: 'clicks',
},
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Click statistics',
},
},
};
And now THE PROBLEM:
On first load, the first line in the dataset, in this case Instagram, shows like this:
The instagram line has a different animation, what could be the problem?
[edit]: Changing the animation in options won't affect the instagram line, it will still come from the left, depending on the animation, it will come either slower or faster, but always from left.
For anyone who has the same issue in the future, here is the solution:
The problem with my code was that the first state of the instaDataClick variable was an empty array, it should have a date property, because the x axis is the date.
so the variable should look something like this:
const initialStartDate = new Date(moment().subtract(10, 'd').format('YYYY-MM-DD'))
const initialEndDate = new Date(moment().format('YYYY-MM-DD'))
const [instaDataClick, setInstaDataClick] = useState(
getDaysArray(initialStartDate.toString(), initialEndDate.toString()).map((e)=>{
return {clicks: null, date: e}
})
)
the function getDaysArray:
const getDaysArray = function (start: any, end: any) {
for (var arr = [], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)) {
arr.push(new Date(dt).toString().substr(4, 6));
}
return arr;
};
And by doing this, we're setting the initial position of the line.

Struggling to correctly update and re-render chart on react-chartjs-2

I am building an app for gym-goers to record and track their progress. For each exercise they input, it should render a chart showing their previous track record. This is working fine.
Users can then add another entry to this track record, but it does not update the chart unless you refresh the page. I can't work out why or how to fix it.
There are a number of different components involved - a parent Exercise.js one, then an ExerciseFooter.js one, which contains the buttons to adjust the target or add a new entry to the exercise, and then AddHistory.js and SetTarget.js components which contain modals and the logic to update the exercise via Redux and MongoDB.
A minimal version of the Exercise.js page is here (I've collapsed the stuff that's mainly styling into single lines as much as possible):
import React, { useState, useEffect } from "react";
import { ExerciseFooter } from "./ExerciseFooter";
import { Line } from "react-chartjs-2";
import { useLocation } from "react-router-dom";
import { useSelector } from "react-redux";
export const Exercise = (props) => {
const location = useLocation();
const users = useSelector((state) => state.auth);
const localUser = JSON.parse(localStorage.getItem("profile"));
const [user, setUser] = useState("");
const [exerciseProp, setExerciseProp] = useState({
history: [""],
target: 0,
});
useEffect(() => {
localUser &&
localUser?.result &&
users.length > 0 &&
setUser(
users.filter(
(filteredUser) => filteredUser._id == props.match.params.userId
)[0]
);
if (!localUser) setUser("");
setExerciseProp(
user?.exercises?.filter(
(exercise) => exercise._id == props.match.params.exerciseId
)[0]
);
}, [users, location]);
//styling for chart
const [barData, setBarData] = useState({
labels: [""],
datasets: [
{ label: "Target", fill: false, radius: 0, data: [""], borderColor: ["rgba(35, 53, 89)"], borderWidth: [3], },
{ label: "You did", data: [""], tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3, },
],
});
//updating chart data
var weightArr = [];
var dateArr = [];
var targetArr = [];
if (exerciseProp) {
exerciseProp.history.map((hist) =>
weightArr.push(parseInt(hist.weight) || 0)
);
exerciseProp.history.map((hist) => dateArr.push(hist.date));
for (let i = 0; i < exerciseProp.history.length; i++) {
targetArr.push(exerciseProp.target);
}
}
useEffect(() => {
if (exerciseProp) {
setBarData({
labels: dateArr,
datasets: [
{
label: "Target",
fill: false,
radius: 0,
data: targetArr,
borderColor: ["rgba(35, 53, 89)"], borderWidth: [3],
},
{
label: "Weight",
data: weightArr,
tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3,
},
],
});
}
}, [users]);
//render chart ones exerciseProp is populated
if (exerciseProp) {
return (
<div style={{ marginTop: "200px" }}>
<Line
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}
/>
{exerciseProp && <ExerciseFooter user={user} exercise={exerciseProp} />}
</div>
);
} else {
return <>Loading...</>;
}
};
I've tried doing a few different things but nothing has worked. I tried adding an 'update' state variable which was updated by a function passed down to the the various dispatches, and then added it to the dependencies of the useEffects, but that didn't seem to make any difference.
Any help much appreciated! As I say, if I just force a refresh then it works fine but know that's bad practice so trying to work out why it isn't re-rendering correctly.
Thanks!
You just have to enable redraw prop
like this
<Line
redraw={true}
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}/>
this all you have to do
redraw={true}

Set Tooltip over line Chartjs

I wanna show tooltips over the line not only on data points.
I also tried the chartjs-plugin-crosshair but it doesn't work in V3 of chartjs.
You can write a custom implementation for V3 for it:
// Options for the indicators
const indicatorOptions = {
radius: 4,
borderWidth: 1,
borderColor: 'red',
backgroundColor: 'transparent'
};
// Override getLabelAndValue to return the interpolated value
const getLabelAndValue = Chart.controllers.line.prototype.getLabelAndValue;
Chart.controllers.line.prototype.getLabelAndValue = function(index) {
if (index === -1) {
const meta = this.getMeta();
const pt = meta._pt;
const vScale = meta.vScale;
return {
label: 'interpolated',
value: vScale.getValueForPixel(pt.y)
};
}
return getLabelAndValue.call(this, index);
}
// The interaction mode
Chart.Interaction.modes.interpolate = function(chart, e, option) {
const x = e.x;
const items = [];
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
const pt = meta.dataset.interpolate({
x
}, "x");
if (pt) {
const element = new Chart.elements.PointElement({ ...pt,
options: { ...indicatorOptions
}
});
meta._pt = element;
items.push({
element,
index: -1,
datasetIndex: meta.index
});
} else {
meta._pt = null;
}
}
return items;
};
// Plugin to draw the indicators
Chart.register({
id: 'indicators',
afterDraw(chart) {
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
if (meta._pt) {
meta._pt.draw(chart.ctx);
}
}
},
afterEvent(chart, args) {
if (args.event.type === 'mouseout') {
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
metas[i]._pt = null;
}
args.changed = true;
}
}
})
var ctx = document.getElementById("myChart").getContext("2d");
var chart = new Chart(ctx, {
type: "line",
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
fill: true,
label: "My First dataset",
backgroundColor: "rgba(132, 0, 0, 1)",
borderColor: "rgb(255, 99, 132)",
data: [0, 10, 5, 2, 20, 30, 45]
},
{
data: [30, 40, 50],
label: 'My Second Dataset',
fill: true,
backgroundColor: "lightgreen",
borderColor: "green"
}
]
},
options: {
interaction: {
mode: "interpolate",
intersect: false,
axis: "x"
},
plugins: {
tooltip: {
displayColors: false,
}
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
<h1>Interpolating line values</h1>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"></canvas>
</div>
The following combination of chartjs-plugin-crosshair and chart.js seems to be working fine for me.
"chart.js": "^3.4.0",
"chartjs-plugin-crosshair": "^1.2.0"
I am initiating the Chart object like below:
Chart.register(CrosshairPlugin);
Which can be used properly in an useEffect block:
useEffect(() =>
Chart.register(CrosshairPlugin);
return () => {
Chart.unregister(CrosshairPlugin);
};
}, []);
And then you can pass the options of the chart like below:
{
...,
options: {
plugins: {
crosshair: {
line: {
color: "#d1d1d1",
width: 1,
},
sync: {
enabled: true,
group: 1,
suppressTooltips: false,
},
zoom: {
enabled: false,
},
}
}
}
}
Note that the configurations above, will keep the crosshair pointer synced over all your charts rendered on the same component. You may need to change the behavior here.
you can use chartjs-plugin-crosshair
function generateDataset(shift, label, color) {
var data = [];
var x = 0;
while (x < 30) {
data.push({
x: x,
y: Math.sin(shift + x / 3)
});
x += Math.random();
}
var dataset = {
backgroundColor: color,
borderColor: color,
showLine: true,
fill: false,
pointRadius: 2,
label: label,
data: data,
lineTension: 0,
interpolate: true
};
return dataset;
}
var chart1 = new Chart(document.getElementById("chart").getContext("2d"), {
type: "scatter",
options: {
plugins: {
crosshair: {
sync: {
enabled: false
},
},
tooltip: {
animation: false,
mode: "interpolate",
intersect: false,
callbacks: {
title: function(a, d) {
return a[0].element.x.toFixed(2);
},
label: function(d) {
return (
d.chart.data.datasets[d.datasetIndex].label + ": " + d.element.y.toFixed(2)
);
}
}
}
},
scales: {
x: {
min: 2,
max: 28
}
}
},
data: {
datasets: [
generateDataset(0, "A", "red")
]
}
});
<script src="https://cdn.jsdelivr.net/npm/moment#2.27.0/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.4.0/dist/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment#0.1.1"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-crosshair#1.2.0/dist/chartjs-plugin-crosshair.min.js"></script>
<canvas id="chart" height="100"></canvas>
https://jsfiddle.net/Lb0k2sqx/1/

Can't perform a React state update on an unmounted component, using class component and component did mount

I have certain code as below:-
class BarChart extends Component {
constructor(){
super();
this.state = {
chartData:{}
}
}
componentDidMount() {
this.getChartData();
}
getChartData() {
axios.get("http://localhost:5001/inventory/clusterscount").then(res => {
const myresponse = res.data;
console.log(myresponse)
let countcluster = [];
let listregion = [];
for (const dataobj of myresponse){
countcluster.push(parseInt(dataobj.clusterscount));
listregion.push(dataobj.region);
}
console.log(countcluster)
console.log(listregion)
this.setState({
chartData: {
labels:listregion,
datasets: [
{
label: "level of thiccness",
data: countcluster,
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
}
});
});
}
render(){
return (
<div className="App">
<h1>Dankmemes</h1>
<div>
<Line
data={this.state.chartData}
options={{
responsive: true,
title: { text: "THICCNESS SCALE", display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 10,
beginAtZero: true
},
gridLines: {
display: false
}
}
],
xAxes: [
{
gridLines: {
display: false
}
}
]
}
}}
/>
</div>
</div>
);
}
}
export default BarChart;
Now while running it am getting the desired clusters and regions as below:-
0: {clusterscount: '2', region: 'test1'}
1: {clusterscount: '10', region: 'test2'}
2: {clusterscount: '8', region: 'test3'}
3: {clusterscount: '1', region: 'test4'}
4: {clusterscount: '8', region: 'test5'}
5: {clusterscount: '2', region: 'test6'}
I am able to get results for clustercount and listregion as well, but keep getting this error. I have tried multiple things but out of ideas.
But in logs am getting as below:-
Can someone help me with this?
The react useEffect expects a cleanup function to cancel subscription and asynchronus tasks so we need to check if component is mounted or not there are couple of ways we can do it and react community have good solution for that.
const LineChart = () =>{
const [chartData,setChartData]= useState({});
const [myresponse, setmyresponse] =useState([]);
const isMountedRef = useRef(null);
useEffect(() =>{
isMountedRef.current = true;
let countcluster = [];
let listregion = [];
axios.get("http://localhost:5001/inventory/clusterscount").
then(res=>{
if(isMountedRef.current){
const myresponse= res.data;
setmyresponse(myresponse)
console.log(myresponse);
for (const dataobj of myresponse){
countcluster.push(parseInt(dataobj.clusterscount));
listregion.push(dataobj.region);
}
setChartData({
labels: listregion,
datasets: [
{
label: "level of thiccness",
data: countcluster,
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
});
}
})
.catch(err=>{
console.log(err);
});
return () => isMountedRef.current = false;
},[])

Resources