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;
Related
I'm trying to share ALL the methods in a function component between another function component, even if the method interacts with the state, here's what I've got working so far:
// App.js
import React, { useRef } from 'react';
import FooColor from './components/FooColor.js';
import BarColor from './components/BarColor.js';
const App = () => {
const fooChangeColorToRed = useRef(() => {});
const fooChangeColorToGreen = useRef(() => {});
const fooChangeColorToBlue = useRef(() => {});
const barChangeColorToRed = useRef(() => {});
const barChangeColorToGreen = useRef(() => {});
const barChangeColorToBlue = useRef(() => {});
return (
<div>
<FooColor
fooChangeColorToRed={fooChangeColorToRed}
fooChangeColorToBlue={fooChangeColorToBlue}
fooChangeColorToGreen={fooChangeColorToGreen}
barChangeColorToRed={barChangeColorToRed}
barChangeColorToGreen={barChangeColorToGreen}
barChangeColorToBlue={barChangeColorToBlue}
/>
<BarColor
fooChangeColorToRed={fooChangeColorToRed}
fooChangeColorToBlue={fooChangeColorToBlue}
fooChangeColorToGreen={fooChangeColorToGreen}
barChangeColorToRed={barChangeColorToRed}
barChangeColorToGreen={barChangeColorToGreen}
barChangeColorToBlue={barChangeColorToBlue}
/>
</div>
);
};
export default App;
// FooColor.js
import React, { useState } from 'react';
const FooColor = (props) => {
const [state, setState] = useState({
color: 'No color picked yet',
});
const changeColorToRed = () => {
setState({
color: 'red',
});
};
const changeColorToGreen = () => {
setState({
color: 'green',
});
};
const changeColorToBlue = () => {
setState({
color: 'blue',
});
};
props.fooChangeColorToRed.current = changeColorToRed;
props.fooChangeColorToGreen.current = changeColorToGreen;
props.fooChangeColorToBlue.current = changeColorToBlue;
return (
<div>
<div style={{ fontSize: 30 }}>Foo</div>
Foo Color: {state.color}
<br />
<button onClick={props.barChangeColorToRed.current}>Change bar color to red</button>
<button onClick={props.barChangeColorToGreen.current}>Change bar color to green</button>
<button onClick={props.barChangeColorToBlue.current}>Change bar color to blue</button>
</div>
);
};
export default FooColor;
// BarColor.js
import React, { useState } from 'react';
const BarColor = (props) => {
const [state, setState] = useState({
color: 'No color picked yet',
});
const changeColorToRed = () => {
console.log('hi');
setState({
color: 'red',
});
};
const changeColorToGreen = () => {
setState({
color: 'green',
});
};
const changeColorToBlue = () => {
setState({
color: 'blue',
});
};
props.barChangeColorToRed.current = changeColorToRed;
props.barChangeColorToGreen.current = changeColorToGreen;
props.barChangeColorToBlue.current = changeColorToBlue;
return (
<div>
<div style={{ fontSize: 30 }}>Bar</div>
Bar Color: {state.color}
<br />
<button onClick={props.fooChangeColorToRed.current}>Change foo color to red</button>
<button onClick={props.fooChangeColorToGreen.current}>Change foo color to green</button>
<button onClick={props.fooChangeColorToBlue.current}>Change foo color to blue</button>
</div>
);
};
export default BarColor;
This has a few issues, sharing more than 10 methods or so becomes very unreadable. Also, the "BarColor" component initially wouldn't be able to call methods in the "FooColor" component until a method is called in the "FooColor" component. This might be because of the order they are placed in inside of the App component
Is there a more efficient and readable way to do the exact same thing?
Why not create a custom hook with as much shared logic as possible
that returns all of your data and needed methods?
For example:
/* color.jsx */
export function useColor() {
// Our shared state:
const [state, setState] = useState({
color: 'No color picked yet',
});
// Our methods:
const changeColorToRed = () => {
setState({
color: 'red',
});
};
const changeColorToGreen = () => {
setState({
color: 'green',
});
};
const changeColorToBlue = () => {
setState({
color: 'blue',
});
};
return {
state,
changeColorToRed,
changeColorToGreen,
changeColorToBlue,
}
}
So basically, we'll have this shared hook containing a shared state and methods,
and we'll return everything we want to use in the relevant comps.
For example:
/* FooColor.jsx */
const FooColor = () => {
const { state, changeColorToRed, changeColorToGreen, changeColorToBlue } = useColor();
return (
<div>
<div style={{ fontSize: 30 }}>Foo</div>
Foo Color: {state.color}
<br />
<button onClick={changeColorToRed}>Change foo color to red</button>
<button onClick={changeColorToGreen}>Change foo color to green</button>
<button onClick={changeColorToBlue}>Change foo color to blue</button>
</div>
);
};
Then you can import and use it in the exact same way in BarColor.
In this example we get a clean result without the need for useRef or even props of any kind in this case.
For more info, you can check out the documentation about custom hooks.
Also, the use-case here is a little vague so I hope that I understood your needs correctly.
Let me know if you need some more help with this.
Update:
If we need 2 separate states for each component color,
we can simply manage separate states in the shared hook.
Example:
// Either like this:
const [fooColor, setFooColor] = useState('No color picked yet');
const [barColor, setBarColor] = useState('No color picked yet');
// Or like this (my personal preference):
const [state, setState] = useState({
fooColor: 'No color picked yet',
barColor: 'No color picked yet',
});
// Then we can add a parameter in order to avoid function duplication:
const changeColorToRed = (comp) => {
switch (comp) {
case 'foo':
setState({
// Spread the existing contents of the state (`barColor` in this case):
...state,
fooColor: 'red',
});
case 'bar':
setState({
...state,
barColor: 'red',
});
default:
console.error('Non-existent comp passed');
}
};
This is my code of dynamic graph, I am using apex chart library,
import React, { Component, useEffect, useState } from "react";
import Chart from "react-apexcharts";
import axios from "axios";
const App = () => {
useEffect(() => {
axios
.get(`https://api.thingspeak.com/channels/1285233/fields/1.json`)
.then((res) => {
console.log("RESPONSE", res.data.feeds);
setData(res.data);
})
.catch((error) => {
});
}, []);
const [data, setData] = useState({});
let series = data.feeds?.map((x) => x.entry_id);
let xaxisdata = []
for(let i = 0; i >= 200 ; i++)
{
xaxisdata.push(i)
}
console.log("SERIES", series);
const options = {
chart: {
id: "basic-bar",
},
xaxis: {
categories: xaxisdata,
},
series: [
{
name: "series-1",
data: series,
},
// {
// name: "series-2",
// data: [10, 80, 60, 75, 35, 72, 8, 42],
// },
],
};
return (
<div className="app">
<div className="row">
<div className="mixed-chart">
<div className="chartPosition">
<Chart
options={options}
series={options.series}
type="line"
width="1000"
/>
</div>
</div>
</div>
</div>
);
};
export default App;
I want to convert my graph into real time changing graph just like the CPU usage graph in task manager it get changes in different time interval. I want that one graph
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
In a React app that uses Highcharts to draw the chart using monthly data, how can we resample the monthly data into quarterly or yearly data, then plot it using Highcharts?
If Highcharts does not provide a resampling function, the solution can use other JS libraries if needed, such as Moment.js. However, Highcharts should be not replaced with another charting library, such as Highstocks.
Thank you in advance!
Original Monthly Data:
const monthlyData = [[946702800000,261],[949381200000,251],[951886800000,282],[954565200000,289],[957153600000,259],[959832000000,259],[962424000000,256],[965102400000,264],[967780800000,289],[970372800000,291],[973054800000,254],[975646800000,275],[978325200000,250],[981003600000,254],[983422800000,227],[986101200000,262],[988689600000,248],[991368000000,237],[993960000000,257],[996638400000,250],[999316800000,239],[1001908800000,253],[1004590800000,235],[1007182800000,259],[1009861200000,259],[1012539600000,264],[1014958800000,243],[1017637200000,240],[1020225600000,214],[1022904000000,238],[1025496000000,251],[1028174400000,255],[1030852800000,259],[1033444800000,248],[1036126800000,260],[1038718800000,268],[1041397200000,246],[1044075600000,259],[1046494800000,264],[1049173200000,273]]
Minimal Reproducible Code
export function Chart({ data }) {
const options = {
title: { text: 'Time series data' },
xAxis: { type: 'datetime' },
}
options.series = [{
name: 'foo',
type: 'line',
data: [[946702800000,261],[949381200000,251],[951886800000,282],[954565200000,289],[957153600000,259],[959832000000,259],[962424000000,256],[965102400000,264],[967780800000,289],[970372800000,291],[973054800000,254],[975646800000,275],[978325200000,250],[981003600000,254],[983422800000,227],[986101200000,262],[988689600000,248],[991368000000,237],[993960000000,257],[996638400000,250],[999316800000,239],[1001908800000,253],[1004590800000,235],[1007182800000,259],[1009861200000,259],[1012539600000,264],[1014958800000,243],[1017637200000,240],[1020225600000,214],[1022904000000,238],[1025496000000,251],[1028174400000,255],[1030852800000,259],[1033444800000,248],[1036126800000,260],[1038718800000,268],[1041397200000,246],[1044075600000,259],[1046494800000,264],[1049173200000,273]],
}]
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
/>
)
}
You need to calculate quarterly and yearly data manually. Below you can find a simple example how you can do that in React. I have used isSameQuarter and isSameYear methods from date-fns library.
function Chart() {
const [options, setOptions] = useState({...});
const getProcessedData = (method) => {
const processedData = [];
const monthlyData = getData();
monthlyData.forEach((el, index) => {
if (
index === 0 ||
!method(new Date(el[0]), new Date(monthlyData[index - 1][0]))
) {
processedData.push(el);
} else {
processedData[processedData.length - 1][1] += el[1];
}
});
return processedData;
};
const yearlyData = useMemo(() => getProcessedData(isSameYear), []);
const quarterlyData = useMemo(() => getProcessedData(isSameQuarter), []);
const setData = (period) => {
const processedData =
period === "month"
? getData()
: period === "year"
? yearlyData
: quarterlyData;
setOptions({
series: [{
data: processedData
}]
});
};
return (
<>
<HighchartsReact highcharts={Highcharts} options={options} />
<button onClick={() => setData("month")}>Monthly Data</button>
<button onClick={() => setData("quarter")}>Quarterly Data</button>
<button onClick={() => setData("year")}>Yearly Data</button>
</>
);
}
Live demo: https://codesandbox.io/s/highcharts-react-demo-forked-w6g6b?file=/demo.jsx
Docs:
https://date-fns.org/v2.17.0/docs/isSameQuarter
https://date-fns.org/v2.17.0/docs/isSameYear
I have the following page in React:
#connect((store) => {
return {
all_orders_data: store.trends.all_orders_data,
...etc...
};
})
class OrdersController extends MyComponent {
constructor(props){
let custom_methods = ['refreshData', 'runDispatch'];
super(props, custom_methods);
this.state = {last_updated_at: new Date()};
}
componentWillMount() {
this.runDispatch();
// this.props.dispatch(fetchOrderData());
}
componentDidMount() {
this.refreshTimerID = setInterval(
() => this.refreshData(),
300 * 1000 // 5 minutes
);
}
refreshData() {
console.log('running refresh');
this.runDispatch(true);
this.setState({last_updated_at: new Date()});
}
runDispatch(refresh=false) {
this.props.dispatch(fetchOrderTrendingAllOrdersData(refresh));
}
render() {
return (
<div>
<h1>Order Trending</h1>
{last_updated_at}
<br/>
<div className="card col-md-12">
<h2 className="style-1">All Orders</h2>
<LineChart
data={all_orders_data}
fetched={fetched_all_orders_data}
error={error_in_fetching_all_orders_data}
/>
</div>
</div>
)
}
In the render I unpack the props, the last_updated header, and render a line chart.
I want to be able to click buttons to toggle lines on the chart. To do this, I have to keep track of the keys for the data variable to show as lines.
I can't put these line options in the constructor because the runDispatch isn't fired off until componentWillMount.
I can't put it in componentWillMount because I don't know when runDispatch will return for data until it does return.
I can get the keys for the all_orders_data in the reducer and pass it to OrdersController as a prop, but props can't be changed and I want this page of our app to control the current lines showing.
I don't want to put it on the chart component because it get's new props every time to refresh runs, and I don't know if it will maintain the proper state after refresh.
The toggle setting doesn't need to be retained later, only while the controller is active (if they pick a new link I don't care if it resets).
My gut is to put state on the line chart since it doesn't have to be permanent.
Where is the correct place to keep the state of these line keys, like:
{
all_orders: ['Online orders', 'Web orders', ...]
}
to let the user toggle what lines he wants to see on graph? It can be on the LineChart, the controller, a new redux prop, etc.
I decided to put it on the LineChart itself, since the buttons toggle lines on it. It wasn't working, but you can protect the state in componentWillReceiveProps:
import React from 'react';
import ReactLoading from 'react-loading';
import {
VictoryChart,
VictoryLine,
VictoryTheme,
VictoryAxis,
VictoryTooltip,
VictoryBar,
VictoryVoronoiContainer
} from 'victory';
import _ from 'underscore';
import MyComponent from '../main/MyComponent';
const COLOR_OPTIONS = [
'#c43a31', // dark red
'blue',
'green',
'yellow',
'purple',
'teal',
'orange'
];
function getTimestringFromUnixTimestamp(timestamp) {
// just default it to AM for now
let period = 'AM'
let date = new Date(timestamp);
let hours = date.getHours();
let minutes = date.getMinutes();
if (hours >= 12) {
period = 'PM';
}
if (hours == 0) {
hours += 12;
} else if (hours >= 13) {
hours -= 12;
}
hours = "0" + hours;
minutes = "0" + minutes;
// Will display time in 10:30 AM format
let formattedTime = `${hours.substr(-2)}:${minutes.substr(-2)} ${period}`;
return formattedTime
}
function displayTooltips(data) {
// x is the unix timestamp, y is the order count
let { x, y } = data;
let formattedTime = getTimestringFromUnixTimestamp(x);
// https://stackoverflow.com/questions/847185/convert-a-unix-timestamp-to-time-in-javascript
return `Time - ${formattedTime}\nOrder Count - ${y}`
}
export default class LineChart extends MyComponent {
constructor(props) {
let custom_methods = [
'generateVictoryLines',
'generateToggleButtons',
'toggleItemOnclick',
'toggleAllLines',
];
super(props, custom_methods);
this.state = {
active_line_keys: [],
available_line_keys: []
};
}
componentWillReceiveProps(nextProps) {
console.log('\n\ncomponentWillReceiveProps:');
let data = nextProps.data;
console.log(data);
if (data) {
let line_keys = Object.keys(data);
console.log('line_keys:');
console.log(line_keys);
console.log('this.state.available_line_keys:');
console.log( this.state.available_line_keys);
let is_equal = _.isEqual(_.sortBy(line_keys), _.sortBy(this.state.available_line_keys));
if (!is_equal) {
console.log('line keys are diff; need to update state');
this.setState({
available_line_keys: line_keys,
active_line_keys: line_keys
});
}
}
}
generateVictoryLines() {
return this.state.active_line_keys.map((key, index) => {
let this_keys_permanent_index = this.state.available_line_keys.indexOf(key);
let color = COLOR_OPTIONS[this_keys_permanent_index];
return (
<VictoryLine
labels={displayTooltips}
labelComponent={<VictoryTooltip/>}
style={{
data: { stroke: `${color}` },
parent: { border: `1px solid #ccc`}
}}
data={this.props.data[key]}
/>
)
});
}
generateToggleButtons() {
return this.state.available_line_keys.map((key, index) => {
let this_keys_permanent_index = this.state.available_line_keys.indexOf(key);
let color = COLOR_OPTIONS[this_keys_permanent_index];
console.log(key);
return (
<button onClick={this.toggleItemOnclick.bind(null, key)} style={ {color: color}}>{key}</button>
);
})
}
toggleItemOnclick(name) {
console.log('\ntoggleItemOnclick:');
console.log(name);
console.log(this.state);
let is_in_active_line_keys = this.state.active_line_keys.indexOf(name) != -1;
console.log(is_in_active_line_keys);
let new_active_line_keys;
if (is_in_active_line_keys) {
new_active_line_keys = this.state.active_line_keys.filter(e => e !== name); // e is each item in the list; filter
// this.setState({active_line_keys: new_active_line_keys});
} else {
new_active_line_keys = this.state.active_line_keys.slice();
new_active_line_keys.push(name);
}
console.log(new_active_line_keys);
this.setState({active_line_keys: new_active_line_keys});
// arr = arr.filter(e => e !== el);
}
toggleAllLines() {
if (this.state.active_line_keys.length < this.state.available_line_keys.length) {
this.setState({active_line_keys: this.state.available_line_keys});
} else {
this.setState({active_line_keys: []});
}
}
// addAllLines() {
// this.setState({active_line_keys: this.state.available_line_keys});
// }
render() {
let graph_body;
let { data, error, fetched } = this.props; // we don't need data here
let victory_lines = this.generateVictoryLines();
let buttons = this.generateToggleButtons();
if (error) {
// alert(error);
console.log('\n\nerror:');
console.log(error);
graph_body = (<h2 className="centered">Error retrieving data</h2>);
} else if (fetched == false) {
graph_body = (
<div className="card col-md-12">
<span><h2 style={{fontWeight: "bold", fontSize: "25px", color: "#F96302"}}>Just a moment...Your request is being processed.</h2>
<ReactLoading type="cylon" color="#F96302" /></span>
</div>
)
} else {
try {
// in the victoryLine, interpolation="natural" will smooth graph out
graph_body = (
<div>
<VictoryChart
theme={VictoryTheme.material}
scale={{x: "time", y: "linear"}}
animate={{duration: 650}}
containerComponent={<VictoryVoronoiContainer/>}
width={1500}
height={600}
>
<VictoryAxis style={ { tickLabels: {fontSize: '20px'} } }/>
<VictoryAxis
dependentAxis
tickValues={[20, 40, 60, 80, 100]}
style={ { tickLabels: {fontSize: '20px'} } }
/>
{victory_lines}
</VictoryChart>
<button onClick={this.toggleAllLines}>Toggle lines</button>
{buttons}
</div>
)
} catch(err) {
graph_body = (<h2 className="centered">Error using response: {`${err}`}</h2>);
}
}
return (
<div>
{graph_body}
</div>
)
}
}