Line chart is not being displayed in Chart.js - javascript

The problem is quite forward, I can't see the line of the graph, and when I press any button. The time of the X-axes should change accordingly to which button is pressed I have been looking through the documentation, for quite some time, but still can't figure it out.
ChartData
import React, { useRef, useEffect, useState } from "react";
import { historyOptions } from '../chartConfig/chartConfig';
import 'chartjs-adapter-moment';
import annotationPlugin from 'chartjs-plugin-annotation';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
Chart.register(annotationPlugin);
const determineTimeFormat = (
timeFormat: string,
day: any,
week: any,
year: any
) => {
switch (timeFormat) {
case "24h":
return day;
case "7d":
return week;
case "1y":
return year;
default:
return day;
}
};
interface Props {
data: any
}
const ChartData: React.FC<Props> = ({ data }) => {
const chartCanvasRef = useRef<HTMLCanvasElement | null>(null);
const { day, week, year, detail } = data;
const [timeFormat, setTimeFormat] = useState("24h");
const [isRebuildingCanvas, setIsRebuildingCanvas] = useState(false);
useEffect(() => {
setIsRebuildingCanvas(true);
}, [timeFormat]);
useEffect(() => {
if (isRebuildingCanvas) {
setIsRebuildingCanvas(false);
}
}, [isRebuildingCanvas]);
useEffect(() => {
if (chartCanvasRef && chartCanvasRef.current && detail) {
const chartCanvas = chartCanvasRef.current
if (isRebuildingCanvas || !chartCanvas) {
return;
}
const chartInstance = new Chart(chartCanvasRef.current, {
type: "line",
data: {
datasets: [
{
label: `${detail.name} price`,
data: determineTimeFormat(timeFormat, day, week, year),
backgroundColor: "rgba(134,159,152, 1)",
borderColor: "rgba(174, 305, 194, 0.4",
},
],
},
Options
options: {
plugins: {
annotation: {
annotations: {
}
}
},
animations: {
tension: {
duration: 1000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
},
maintainAspectRatio: false,
responsive: true,
scales: {
x:
{
type: 'time',
},
},
}
});
return () => {
chartInstance.destroy();
}
}}, [day, isRebuildingCanvas,timeFormat, week, year, detail]);
Rest of the Component code
return (
<div className='chart__container'>
{renderPrice()}
{isRebuildingCanvas ? undefined : (
<canvas ref={chartCanvasRef} id='myChart' width={250} height={250}></canvas>
)}
<button className='time__format' onClick={() => setTimeFormat("24h")}>24h</button>
<button className='time__format' onClick={() => setTimeFormat("7d")}>7d</button>
<button className='time__format' onClick={() => setTimeFormat("1y")}>1y</button>
</div>
);
};
export default ChartData;

Looks like your isRebuildingCanvas logic might be inconsistent, or I don't just understand it.
Anyway, from the Chart.js perspective, you'd want to change the data and call chartInstance.update() when pressing the button that changes the data.
Partial example:
const canvas = useRef(null);
const [chart, setChart] = useState();
const [timeFormat, setTimeFormat] = useState("24h");
useEffect(() => {
if (chart || !canvas.current) return;
const ctx = canvas.current.getContext("2d");
if (!ctx) return;
const config = {/*...*/};
setChart(new Chart(ctx, config));
}, [chart, canvas]);
useEffect(() => {
if (!chart) return;
chart.config.data.datasets[0].data = determineTimeFormat(timeFormat, day, week, year);
chart.update();
}, [chart, timeFormat]);
And a complete, very similar example:
https://codesandbox.io/s/blissful-faraday-hzcq0

Related

How to create a compass that points to specific coordinates (React-Native)

Here is what I have for now:
import {
Alert,
Animated,
Easing,
Linking,
StyleSheet,
Text,
View,
} from "react-native";
import React, { useEffect, useState } from "react";
import * as Location from "expo-location";
import * as geolib from "geolib";
import { COLORS } from "../../assets/Colors/Colors";
export default function DateFinder() {
const [hasForegroundPermissions, setHasForegroundPermissions] =
useState(null);
const [userLocation, setUserLocation] = useState(null);
const [userHeading, setUserHeading] = useState(null);
const [angle, setAngle] = useState(0);
useEffect(() => {
const AccessLocation = async () => {
function appSettings() {
console.warn("Open settigs pressed");
if (Platform.OS === "ios") {
Linking.openURL("app-settings:");
} else RNAndroidOpenSettings.appDetailsSettings();
}
const appSettingsALert = () => {
Alert.alert(
"Allow Wassupp to Use your Location",
"Open your app settings to allow Wassupp to access your current position. Without it, you won't be able to use the love compass",
[
{
text: "Cancel",
onPress: () => console.warn("Cancel pressed"),
},
{ text: "Open settings", onPress: appSettings },
]
);
};
const foregroundPermissions =
await Location.requestForegroundPermissionsAsync();
if (
foregroundPermissions.canAskAgain == false ||
foregroundPermissions.status == "denied"
) {
appSettingsALert();
}
setHasForegroundPermissions(foregroundPermissions.status === "granted");
if (foregroundPermissions.status == "granted") {
const location = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.BestForNavigation,
activityType: Location.ActivityType.Fitness,
distanceInterval: 0,
},
(location) => {
setUserLocation(location);
}
);
const heading = await Location.watchHeadingAsync((heading) => {
setUserHeading(heading.trueHeading);
});
}
};
AccessLocation().catch(console.error);
}, []);
useEffect(() => {
if (userLocation != null) {
setAngle(getBearing() - userHeading);
rotateImage(angle);
}
}, [userLocation]);
const textPosition = JSON.stringify(userLocation);
const getBearing = () => {
const bearing = geolib.getGreatCircleBearing(
{
latitude: userLocation.coords.latitude,
longitude: userLocation.coords.longitude,
},
{
latitude: 45.47200370608976,
longitude: -73.86246549592089,
}
);
return bearing;
};
const rotation = new Animated.Value(0);
console.warn(angle);
const rotateImage = (angle) => {
Animated.timing(rotation, {
toValue: angle,
duration: 1000,
easing: Easing.bounce,
useNativeDriver: true,
}).start();
};
//console.warn(rotation);
return (
<View style={styles.background}>
<Text>{textPosition}</Text>
<Animated.Image
source={require("../../assets/Compass/Arrow_up.png")}
style={[styles.image, { transform: [{ rotate: `${angle}deg` }] }]}
/>
</View>
);
}
const styles = StyleSheet.create({
background: {
backgroundColor: COLORS.background_Pale,
flex: 1,
// justifyContent: "flex-start",
//alignItems: "center",
},
image: {
flex: 1,
// height: null,
// width: null,
//alignItems: "center",
},
scrollView: {
backgroundColor: COLORS.background_Pale,
},
});
I think that the math I'm doing must be wrong because the arrow is pointing random directions spinning like crazy and not going to the coordinate I gave it. Also, I can't seem to use the rotateImage function in a way that rotation would be animated and i'd be able to use it to animate the image/compass. If anyone could help me out i'd really appreciate it I've been stuck on this for literally weeks.

React time lines

I have a big problem with React TimeLines Package(https://openbase.com/js/react-timelines)
I want something like this photo:
( having 3 P tags with different ClassNames)
but in default case of this package I cant do it!
I think I should use something like createElement and textContent in JS. but I dont know how!
My Codes:
import React, { Component } from "react";
import Timeline from "react-timelines";
import "react-timelines/lib/css/style.css";
import { START_YEAR, NUM_OF_YEARS, NUM_OF_TRACKS } from "./constant";
import { buildTimebar, buildTrack } from "./builder";
import { fill } from "./utils";
const now = new Date("2021-01-01");
const timebar = buildTimebar();
// eslint-disable-next-line no-alert
const clickElement = (element) =>
alert(`Clicked element\n${JSON.stringify(element, null, 2)}`);
class App extends Component {
constructor(props) {
super(props);
const tracksById = fill(NUM_OF_TRACKS).reduce((acc, i) => {
const track = buildTrack(i + 1);
acc[track.id] = track;
return acc;
}, {});
this.state = {
open: false,
zoom: 2,
// eslint-disable-next-line react/no-unused-state
tracksById,
tracks: Object.values(tracksById),
};
}
handleToggleOpen = () => {
this.setState(({ open }) => ({ open: !open }));
};
handleToggleTrackOpen = (track) => {
this.setState((state) => {
const tracksById = {
...state.tracksById,
[track.id]: {
...track,
isOpen: !track.isOpen,
},
};
return {
tracksById,
tracks: Object.values(tracksById),
};
});
};
render() {
const { open, zoom, tracks } = this.state;
const start = new Date(`${START_YEAR}`);
const end = new Date(`${START_YEAR + NUM_OF_YEARS}`);
return (
<div className="app">
<Timeline
scale={{
start,
end,
zoom,
}}
isOpen={open}
toggleOpen={this.handleToggleOpen}
clickElement={clickElement}
timebar={timebar}
tracks={tracks}
now={now}
enableSticky
scrollToNow
/>
</div>
);
}
}
export default App;
builder.js:
export const buildElement = ({ trackId, start, end, i }) => {
const bgColor = nextColor();
const color = colourIsLight(...hexToRgb(bgColor)) ? "#000000" : "#ffffff";
return {
id: `t-${trackId}-el-${i}`,
title: "Bye Title: Hello Type: String",
start,
end,
style: {
backgroundColor: `#${bgColor}`,
color,
borderRadius: "12px",
width: "auto",
height: "120px",
textTransform: "capitalize",
},
};
};

Having problems with Chart.js and Canvas

I am currently using Graph.js to render graphs it is working on the initial render, but until I press setTimeformats buttons in order to show another graph on the same canvas, it is giving me Error: Canvas is already in use. Chart with ID '0' must be destroyed before the canvas can be reused. Am I using it properly? How Should I destroy the chart in order to use other graphs on the same canvas? Help would be very appreciated.
import React, { useRef, useEffect, useState } from "react";
import { historyOptions } from "../chartConfig/chartConfig";
import Chart from 'chart.js/auto';
interface Props{
data:any
}
const ChartData:React.FC<Props> = ({ data}) => {
const chartRef = useRef<HTMLCanvasElement | null>(null);
const { day, week, year, detail } = data;
const [timeFormat, setTimeFormat] = useState("24h");
const determineTimeFormat = () => {
switch (timeFormat) {
case "24h":
return day;
case "7d":
return week;
case "1y":
return year;
default:
return day;
}
};
useEffect(() => {
if (chartRef && chartRef.current && detail) {
const chartInstance = new Chart(chartRef.current, {
type: "line",
data: {
datasets: [
{
label: `${detail.name} price`,
data: determineTimeFormat(),
backgroundColor: "rgba(174, 305, 194, 0.5)",
borderColor: "rgba(174, 305, 194, 0.4",
pointRadius: 0,
},
],
},
options: {
...historyOptions,
},
});
if (typeof chartInstance !== "undefined") chartInstance.destroy();
}
});
const renderPrice = () => {
if (detail) {
return (
<>
<p className="my-0">${detail.current_price.toFixed(2)}</p>
<p
className={
detail.price_change_24h < 0
? "text-danger my-0"
: "text-success my-0"
}
>
{detail.price_change_percentage_24h.toFixed(2)}%
</p>
</>
);
}
};
return (
<div className="bg-white border mt-2 rounded p-3">
<div>{renderPrice()}</div>
<div>
<canvas ref={chartRef} id="myChart" width={250} height={250}></canvas>
</div>
<div className="chart-button mt-1">
<button
onClick={() => setTimeFormat("24h")}
className="btn btn-outline-secondary btn-sm"
>
24h
</button>
<button
onClick={() => setTimeFormat("7d")}
className="btn btn-outline-secondary btn-sm mx-1"
>
7d
</button>
<button
onClick={() => setTimeFormat("1y")}
className="btn btn-outline-secondary btn-sm"
>
1y
</button>
</div>
</div>
);
};
export default ChartData;
One way you might solve this problem is by using a new state variable and useEffect to quickly remove and re-create the canvas element each time the timeFormat changes. Some key points here:
As #CallumMorrisson mentioned, in order to understand this approach, it is extremely important to read and understand this section of the React docs about skipping the useEffect hook in its entirety.
Using the day, name, week, year attributes directly in useEffect instead of the entire data variable makes sure that the chart instance is only re-created when necessary, not on every render. Same goes for the function determineTimeFormat, those types of functions should be defined outside the component's scope if possible.
const determineTimeFormat = (
timeFormat: string,
day: any,
week: any,
year: any
) => {
switch (timeFormat) {
case "24h":
return day;
case "7d":
return week;
case "1y":
return year;
default:
return day;
}
};
interface Props {
data: any
}
const ChartData: React.FC<Props> = ({ data }) => {
const chartCanvasRef = useRef<HTMLCanvasElement | null>(null);
const { day, week, year, detail } = data;
const { name } = detail;
const [timeFormat, setTimeFormat] = useState("24h");
const [isRebuildingCanvas, setIsRebuildingCanvas] = useState(false);
// remove the canvas whenever timeFormat changes
useEffect(() => {
setIsRebuildingCanvas(true);
}, [timeFormat]); // timeFormat must be present in deps array for this to work
/* if isRebuildingCanvas was true for the latest render,
it means the canvas element was just removed from the dom.
set it back to false to immediately re-create a new canvas */
useEffect(() => {
if (isRebuildingCanvas) {
setIsRebuildingCanvas(false);
}
}, [isRebuildingCanvas]);
useEffect(() => {
const chartCanvas = chartCanvasRef.current
if (isRebuildingCanvas || !chartCanvas) {
return;
}
const chartInstance = new Chart(chartRef.current, {
type: "line",
data: {
datasets: [
{
label: `${name} price`,
data: determineTimeFormat(timeFormat, day, week, year),
backgroundColor: "rgba(174, 305, 194, 0.5)",
borderColor: "rgba(174, 305, 194, 0.4",
pointRadius: 0,
},
],
},
options: {
...historyOptions,
},
});
return () => {
chartInstance.destroy();
}
}, [day, isRebuildingCanvas, name, timeFormat, week, year]);
return (
<>
{isRebuildingCanvas ? undefined : (
<canvas ref={chartCanvasRef} id='myChart' width={250} height={250} />
)}
<button onClick={() => setTimeFormat("24h")}>24h</button>
<button onClick={() => setTimeFormat("7d")}>7d</button>
<button onClick={() => setTimeFormat("1y")}>1y</button>
</>
);
};
export default ChartData;

Data visualization in ReactJs with ChartJs

I am new in reactjs. Currently I'm developing an app which shows json COVID-19 api data into visualization using chartjs. I tried this from yesterday but I can't show the visual data.
Here is my code
App Component
import React, { useState, useEffect } from "react";
import axios from "axios";
import Chart from "./Chart";
const App = () => {
const [state, setState] = useState({});
const [loading, setLoading] = useState(true);
const [chart, setChart] = useState({});
useEffect(() => {
getData("italy");
setChart({
labels: ["Cases", "Deaths", "Recovered"],
datasets: [
{
label: "cases",
data: [state.cases]
},
{
label: "deaths",
data: [state.deaths]
},
{
label: "recovered",
data: [state.recovered]
}
]
});
}, []);
const getData = async country => {
try {
const res = await axios.get(
`https://corona.lmao.ninja/v2/historical/${country}`
);
setLoading(false);
setState(res.data.timeline);
} catch (error) {
console.log(error.response);
}
};
return (
<div>
{!loading
? console.log(
"cases",
state.cases,
"deaths",
state.deaths,
"recovered",
state.recovered
)
: null}
{!loading ? <Chart chart={chart} /> : "loading failed"}
</div>
);
};
export default App;
And Here is Chart Component
import React from "react";
import { Line } from "react-chartjs-2";
const Chart = ({chart}) => {
return (
<div>
<Line
data={chart}
height={300}
width={200}
options={{
maintainAspectRatio: false,
title: {
display: true,
text: "Covid-19",
fontSize: 25
},
legend: {
display: true,
position: "top"
}
}}
/>
</div>
);
};
export default Chart;
If I open browser and dev tools it look likes this
I want to visualize the data like this
Here is codeSandBox.io
Looks like data property within dataset takes only array of numbers. I have simplifies your code using class based component. It will help you get started.
https://codesandbox.io/s/react-chartjs-2-example-mzh9o
setChartData = () => {
let { data } = this.state;
let chartData = {
labels: ["Cases", "Deaths", "Recovered"],
datasets: [
{
label: "cases",
data: Object.values(data.cases)
},
{
label: "deaths",
data: Object.values(data.deaths)
},
{
label: "recovered",
data: Object.values(data.recovered)
}
]
};
this.setState({
chart: chartData
});
};

highcharts typeerror this is not a function

I have a highcharts function that populates a chart. It also has a drilldown event that needs to be called when the drilldown event is triggered.
I get this following error when I drilldown the charts-
TypeError: this.filter_data is not a function
The drilldown event is in the populate_group_by_gender_chart function under the options/chart/events.
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Container, } from 'react-bootstrap';
import Header from './Components/Header';
import Wrapper from './Components/Wrapper';
import _ from 'lodash';
import './App.css';
let county_data = 'https://data.m.gov/api/views/kdqy-4wzv/rows.json?accessType=DOWNLOAD'
class App extends Component {
constructor(props){
super(props)
this.state = {
raw_data: [],
employee_data: [],
column_names: [],
page_num: 1,
group_by_gender_data: {}
}
}
componentDidMount = () => {
this.fetchData();
}
fetchData = async () => {
fetch(county_data).
then(response => response.json()).
then(response => {
// console.log(response.data)
let column_names = _.map(response.meta.view.columns, 'name')
const data_map = response.data.map(data=> {
return {
id: data[0],
'full_name':data[8],
'gender':data[9],
'current_annual_salary':data[10],
'2018_gross_pay_received':data[11],
'2018_overtime_pay':data[12],
'department':data[13],
'department_name':data[14],
'division':data[15],
'assignment_category':data[16],
'employee_position_title':data[17],
'position_under_filled':data[18],
'date_first_hired':data[19]
}
})
// console.log('data_map - ', data_map)
let grouped_data_by_chunks = _.chunk(data_map, data_map.length/100)
// console.log('grouped_data -', grouped_data_by_chunks)
this.setState({
raw_data: data_map,
employee_data: grouped_data_by_chunks[0],
column_names: column_names
})
this.populate_group_by_gender_chart(data_map);
})
}
populate_group_by_gender_chart = (data) => {
console.log('drilldown this.filter_data -', this.filter_data)
var options = {
chart: {
type: "pie",
events:{
drilldown: function(e){
console.log('e.point.name - ', e.point.name)
var filter_by = (e.point.name = 'Female') ? 'F': 'M'
this.filter_data('GENDER', data, filter_by)
}
}
},
title: {
text: 'Employees by Gender'
},
series: [
{
name: 'Gender',
data: []
}
],
plotOptions: {
series: {
cursor: 'pointer',
point: {}
},
},
};
options.series[0].data = this.filter_data('GENDER', data)
this.setState({
group_by_gender_options: options
})
}
filter_data = (filter, data, filter_by) => {
if (filter == 'GENDER'){
data = _.map(data, 'gender')
data = _.filter(data, function(val){
return val == 'F';
})
const result = _.values(_.groupBy(data)).map(d => ({'y' : d.length,
'name' : d[0] == 'F' ? 'Female': 'Male',
'id': d[0] == 'F' ? 'F': 'M',
'drilldown': true
}));
return result
}
}
render() {
return (
<div className="App">
<Container fluid={true}>
<Header/>
<Wrapper data={this.state.employee_data}
group_by_gender_chart_data={this.state.group_by_gender_options}
></Wrapper>
</Container>
</div>
);
}
}
export default App;

Categories

Resources