I'm using Chart.js inside my react project.
and i want to structure my project as neatly as possible by creating components
and calling them on my App.js
How can I go about creating a react prop and calling it from another component without Actually calling my pie chart from the App.js
import React from 'react';
import {Pie} from 'react-chartjs-2';
const state = {
labels: ['January', 'February', 'March',
'April', 'May'],
datasets: [
{
label: 'Rainfall',
backgroundColor: [
'#B21F00',
'#C9DE00',
'#2FDE00',
'#00A6B4',
'#6800B4'
],
hoverBackgroundColor: [
'#501800',
'#4B5000',
'#175000',
'#003350',
'#35014F'
],
data: [65, 59, 80, 81, 56]
}
]
}
export default class App extends React.Component {
render() {
return (
<div>
<Pie
data={state}
options={{
title:{
display:true,
text:'Average Rainfall per month',
fontSize:20
},
legend:{
display:true,
position:'left',
}
}}
/>
</div>
);
}
}
If you create a component named PieChart in the PieChart.js file like this:
import React from 'react';
import { Pie } from 'react-chartjs-2';
export default class PieChart extends React.Component {
render() {
return (
<div>
<Pie
data={this.props.data}
options={{
title: {
display: true,
text: 'Average Rainfall per month',
fontSize: 20
},
legend: {
display: true,
position: 'left',
}
}}
/>
</div>
);
}
}
Then you can import it from the App.js in this way:
import React from 'react';
import PieChart from './PieChart';
const state = {
labels: ['January', 'February', 'March',
'April', 'May'],
datasets: [
{
label: 'Rainfall',
backgroundColor: [
'#B21F00',
'#C9DE00',
'#2FDE00',
'#00A6B4',
'#6800B4'
],
hoverBackgroundColor: [
'#501800',
'#4B5000',
'#175000',
'#003350',
'#35014F'
],
data: [65, 59, 80, 81, 56]
}
]
}
export default class App extends React.Component {
render() {
return (
<PieChart data={state} />
)
}
}
Here I've created a custom props named data and passed it to the PieChart component which is being accessed by this.props.data in the PieChart component. You can create multiple props as you want. Like you can pass options as well in this way.
Also you can keep your data(which is passed from the App as data props) in the PieChart component and call it from anywhere you want by <PieChart />.
Note: I've kept App.js and PieChart.js both files in the same directory.
This how I used piechart in my project:
in another file, you can create a custom component and add your code like below
export const StatusChart = ({ data }) => {
const [pieData, setPieData] = useState({});
useEffect(() => {
setPieData({
labels: ['declined', 'accepted', 'goterror', 'inprogress'],
datasets: [
{
data: [data.inprogress, data.got_error, data.accepted, data.declined],
backgroundColor: ['#F7464A', '#46BFBD', '#FDB45C', '#949FB1', '#4D5360'],
hoverBackgroundColor: ['#FF5A5E', '#5AD3D1', '#FFC870', '#A8B3C5', '#616774'],
},
],
});
}, [data]);
return (
<MDBContainer>
<Pie data={pieData} options={{ responsive: true }} />
</MDBContainer>
);
};
and in App.js you can use useState to save data like below:
const [generalData, setGeneralData] = useState(null);
and use it in your code:
{generalData ? <StatusChart data={generalData.status_chart} /> : <Spinner animation="border" />}
Related
I'm trying to render a graph with ionic react but i keep getting errors such as:
Uncaught Error: Canvas is already in use. Chart with ID '0' must be
destroyed before the canvas with ID '' can be reused.
import { Bar } from "react-chartjs-2";
const Graph: React.FC = (props) => {
const data = {
labels: ["Billable", "Non Billable"],
datasets: [
{
label: "Billable Vs. Non Billable",
backgroundColor: ["#36a2eb", "rgba(255,99,132,0.2)"],
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: [65, 59],
},
],
};
return (
<div>
<h2>bar example</h2>
<Bar
data={data}
width={100}
height={50}
options={{ maintainAspectRatio: false }}
/>
</div>
);
};
export default Graph;
does anyone knows how to solve it?
I have found a solution, credit to #TechMaze from this post.
I had to import CategoryScale from chart.js and Chart from chart.js/auto.
it seems that Chart from chart.js wont solve this issue(!)
import { Bar } from "react-chartjs-2";
import { CategoryScale } from "chart.js";
import { Chart as ChartJS } from "chart.js/auto";
import {
IonTitle,
useIonViewWillEnter,
useIonViewWillLeave,
} from "#ionic/react";
const Graph: React.FC = () => {
useIonViewWillEnter(() => {
ChartJS.register(CategoryScale);
}, []);
useIonViewWillLeave(() => {
ChartJS.unregister(CategoryScale);
}, []);
const data = {
labels: ["Billable", "Non Billable"],
datasets: [
{
label: "Billable Vs. Non Billable",
backgroundColor: ["#36a2eb", "rgba(255,99,132,0.2)"],
borderColor: "rgba(255,99,132,1)",
borderWidth: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: [65, 59],
},
],
};
return (
<>
<IonTitle>Bar Example</IonTitle>
<Bar data={data} />
</>
);
};
export default Graph;
With this code:
import ReactEChartsCore from 'echarts-for-react/lib/core';
// Import the echarts core module, which provides the necessary interfaces for using echarts.
import * as echarts from 'echarts/core';
// import components, all suffixed with Component
import {
GridComponent,
TooltipComponent,
TitleComponent,
DatasetComponent,
} from 'echarts/components';
// Import renderer, note that introducing the CanvasRenderer or SVGRenderer is a required step
import {
CanvasRenderer,
} from 'echarts/renderers';
function App_D() {
const options = {
grid: { top: 8, right: 8, bottom: 24, left: 36, containLabel: true },
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: {
type: 'value',
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
//type: 'bar',
//type: 'radar',
smooth: true,
},
],
tooltip: {
trigger: 'axis',
},
};
return (
<div className='container'>
<h1 className='heading'>
Data & Context Visualization
</h1>
<ReactECharts option={options} />;
</div>
);
}
export default App_D;
I get a correct chart.
But with this code:
echarts.tsx :
import ReactECharts from 'echarts-for-react'
// import the core library.
import ReactEChartsCore from 'echarts-for-react/lib/core';
// Import the echarts core module, which provides the necessary interfaces for using echarts.
import * as echarts from 'echarts/core';
// import components, all suffixed with Component
import {
GridComponent,
TooltipComponent,
TitleComponent,
DatasetComponent,
} from 'echarts/components';
// Import renderer, note that introducing the CanvasRenderer or SVGRenderer is a required step
import {
CanvasRenderer,
} from 'echarts/renderers';
interface Props {
chartType: string;
seriesData: number[];
xAxisType: string;
xAxisData: string[];
}
export default function EchartDeploy({
chartType,
seriesData,
xAxisType,
xAxisData
}: Props) {
React.useEffect(() => {
echarts.use(
[TitleComponent, TooltipComponent, GridComponent, CanvasRenderer]
)
}, [])
const options = {
grid: { top: 8, right: 8, bottom: 24, left: 36, containLabel: true },
aAxis: {
type: xAxisType,
data: xAxisData,
},
yAxis: {
type: 'value',
},
series: [
seriesData,
chartType,
],
tooltip: {
trigger: 'axis',
},
}
return (
<ReactECharts option={options} />
);
}
App_D.tsx :
import EchartDeploy from './dataVisualize/echarts'
function App_D() {
let [chart_type, setChart_type] = React.useState("")
let [series_data, setSeries_data] = React.useState<number[]>([])
let [xAxis_type, setXAxis_type] = React.useState("")
let [xAxis_data, setXAxis_data] = React.useState<string[]>([])
setChart_type('line')
setSeries_data([820, 932, 901, 934, 1290, 1330, 1320])
setXAxis_type('category')
setXAxis_data(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'])
return (
<div className='container'>
<h1 className='heading'>
Data & Context Visualization
</h1>
<EchartDeploy
chartType={chart_type}
seriesData={series_data}
xAxisType={xAxis_type}
xAxisData={xAxis_data}
/>
</div>
);
}
export default App_D;
I get no errors messages anymore, but also no chart.
What am I doing wrongly? How to pass the parameters to the React functional component?
function App_D() {
//here should be defind all of yours useStates() for chart_type/series_data etc.
useEffect(() => {
//all of your setters
}, []); //with an empty dependency array
return (
<div className='container'>
<h1 className='heading'>
Data & Context Visualization
</h1>
<EchartDeploy
chartType={chart_type}
seriesData={series_data}
xAxisType={xAxis_type}
xAxisData={xAxis_data}
/>
</div>
)
I am trying to populate chart data from my backend.
Although I am fetching data and pushing data in componentDidMount, the Bars or Scatters are not loaded on page load.
If I change my screen width in inspect mode in google dev tools, it starts loading which leads me to believe this is a lifecyle problem.
However, changing it to componentWillMount did not change anything. Putting a if statement before render like below just stops loading the chart altogether.
if(this.state.data.datasets[0].data.length ===0){
return null;
}
Any way to fix this problem?
import React, { Component } from "react";
import axios from "axios";
import { Bar, Scatter } from "react-chartjs-2";
export class Data extends Component {
constructor(props) {
super(props);
this.state = {
provider: [],
data: {
labels: ["Action", "Anime", "Children"],
datasets: [
{
label: "Total",
backgroundColor: "rgba(255, 159, 64, 0.4)",
borderColor: "white",
borderWidth: 1,
stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: []
},
{
label: "Above ⭐️8.5",
backgroundColor: "white",
type: "scatter",
showLine: false,
stack: 1,
data: []
}
]
}
};
}
componentDidMount() {
axios.get("http://localhost:8001/provider").then(res =>
this.setState({ provider: res.data }, () => {
this.pushAction();
})
);
}
pushAction() {
const dataState = this.state.data;
const oldDataTotal = this.state.data.datasets[0].data;
const oldDataGood = this.state.data.datasets[1].data;
oldDataTotal.push(this.state.provider[0].huluAction);
oldDataTotal.push(this.state.provider[0].huluAnime);
oldDataTotal.push(this.state.provider[0].huluChildren);
oldDataGood.push(this.state.provider[0].Action);
oldDataGood.push(this.state.provider[0].Anime);
oldDataGood.push(this.state.provider[0].Children);
}
render() {
console.log(this.state.musicalData);
const options = {
responsive: true,
maintainAspectRatio: false,
legend: {
display: true
},
type: "bar",
scales: {
xAxes: [
{
stacked: true
}
],
yAxes: [
{
stacked: true
}
]
}
};
return (
<div>
<Bar data={this.state.data} height={300} options={options} />
</div>
);
}
}
export default Data;
you have to update the state after changing the datasets,
pushAction = () => {
const dataState = this.state.data;
const oldDataTotal = this.state.data.datasets[0].data;
const oldDataGood = this.state.data.datasets[1].data;
oldDataTotal.push(this.state.provider[0].huluAction);
oldDataTotal.push(this.state.provider[0].huluAnime);
oldDataTotal.push(this.state.provider[0].huluChildren);
oldDataGood.push(this.state.provider[0].Action);
oldDataGood.push(this.state.provider[0].Anime);
oldDataGood.push(this.state.provider[0].Children);
this.setState({data: {...dataState, datasets : [...oldDataTotal, oldDataGood]}});
}
I'm trying to figure out how to get ag-grid working with Typescript React. I'm trying to make a table with a custom column type. The custom column type and the default column type I'm defining don't seem to be working and I'm not sure why. Here's a simple version of my code:
import React from 'react';
import './App.css';
import { AgGridReact } from 'ag-grid-react';
import { GridOptions } from "ag-grid-community";
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham.css';
class App extends React.Component<{}, GridOptions> {
constructor(props: GridOptions) {
super(props);
this.state = {
rowData: [
{foo: 1, bar: 'abc'},
{foo: 2, bar: 'def'},
],
columnDefs: [
{headerName: "foo", field: "foo", type: "numberColumn"},
{headerName: "bar", field: "bar"},
],
defaultColDef: {
width: 150,
editable: true,
sortable: true,
filter: "agTextColumnFilter"
},
columnTypes: {
"numberColumn": {
width: 20,
filter: "agNumberColumnFilter"
},
},
onCellClicked: function(event) {window.alert('a row was clicked '+ event.data)},
}
}
render() {
return (
<div
className="ag-theme-balham"
style={{
height: '1000px',
width: '6000px' }}
>
<AgGridReact
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}>
</AgGridReact>
</div>
);
}
}
export default App;
I also found that onCellClicked isn't actually doing anything, so I think I'm misunderstanding something there.
You are defining in your local state but you are not passing it to AgGridReact component this it has no knowledge of it.
You can destructure state properties like below:
const {columnDefs, rowData, defaultColDef, onCellClicked, columnTypes} = this.state
And then use it and pass it like below:
<AgGridReact
columnDefs={columnDefs}
rowData={rowData}
defaultColDef={defaultColDef}
onCellClicked={onCellClicked}
columnTypes={columnTypes}
>
</AgGridReact>
I'm trying to add a bar chart to my app with arbitrary data for now but I can't get it to render. The it's imported correctly and i've used example code from the chart.js web site but no luck. What am I missing?
import Chart from 'chart.js';
import React, { Component } from 'react';
class ChartTeamOne extends Component {
setState(){
const t1ChartEl= document.getElementById("teamOneCanvas");
let teamOneChart = new Chart(t1ChartEl,{
type: 'bar',
data: {
labels: ["Red", "Blue"],
datasets: [{
label: '# of Votes',
data: [12, 19],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
}
render(){
return(
<canvas id="teamOneCanvas"></canvas>
);
}
}
export default ChartTeamOne;
This is because you have this in the setState function and this is not setting the state of anything. This should be in the componentDidMount() lifecycle function.
setState() is used to update the state of your application https://facebook.github.io/react/docs/component-api.html where as componentDidMount() will fire once your component is mounted after the initial rendering occurs and will perform the functions inside. https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount
chart.js needs a canvas to "overwrite" before it can draw its chart. You have the part where you return the canvas correct. But the function you are looking for is componentDidMount()not setState(). I have taken their example for pie charts and created a react class for it. It should be pretty simple to change your code to match the change.
import Chart from 'chart.js';
import React, {
Component
} from 'react';
export default class PieChart extends Component {
componentDidMount() {
debugger;
const ctx = document.getElementById("pie-chart");
const data = {
labels: [
"Red",
"Blue",
"Yellow"
],
datasets: [{
data: [300, 50, 100],
backgroundColor: [
"#FF0000",
"#0000FF",
"#FFCE56"
],
hoverBackgroundColor: [
"#FF6384",
"#36A2EB",
"#FFCE56"
]
}]
};
const pieChart = new Chart(ctx, {
type: 'pie',
data: data
});
}
render() {
return (
<canvas id="pie-chart"></canvas>
);
}
}