I'm creating a React Component in typescript for each Chart type in chart.js. Most of it is working out fine. However, I've hit my first roadblock as I just remembered that you can have multiple datasets within a chart.
I have the following code for a Bar Chart
export class Bar extends React.Component<IBarProps, {}> {
constructor(props: IBarProps) {
super(props)
}
public render() {
const className = classNames('bar-chart', this.props.className)
const ctx = document.getElementById('bar-chart') as HTMLCanvasElement
let bar: Chart = new Chart(ctx, {
type: this.props.horizontal ? 'horizontalBar' : 'bar',
data: {
labels: this.props.labels,
datasets: [
{
label: this.props.label,
xAxisID: this.props.xAxisID,
yAxisID: this.props.yAxisID,
backgroundColor: this.props.backgroundColor,
borderColor: this.props.borderColor,
borderWidth: this.props.borderWidth
}
]
} as ChartData,
options: {
elements: { rectangle: { borderSkipped: this.props.borderSkipped } }
}
})
bar.update()
return (
<div className={className}>
<canvas
id="bar-chart"
width={this.props.width ? this.props.width : '400'}
height={this.props.height ? this.props.height : '400'}
/>
</div>
)
}
// private push() {
// const dataSets = this.props.dataSets
// dataSets.forEach(dataSet => {new Object(label: this.props.label,)})
// return dataSets
// }
}
The issue is that the way I've set it up, it'd only support a single dataset. What i need is that for each dataset that is defined, create an object for it and push it into the datasets array. However I'm not a Javascript expert nor typescript expert.
Would anyone be able to show me how to setup the dynamic datasets array? If you need more info see the Bar Chart Docs
Related
I'm making a chart and im kind of confused. ill attach a photo below
So first off, I want the title to not be undefined, how do I change the title? And I want to change the background color of the chart below the lines, heres my code
{data?.length > 0 && (
<Line
options={options}
height={"300%"}
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data,
}
]
}}
/>
)}
The legend label will be correctly displayed if you define label on your dataset, this was mentioned by WhiteHat in his comment.
In order to obtain the background color below the lines (basically an area chart), you need to add fill: true to the dataset.
To make it work, you also need to import and register Filler from chart.js.
Please take a look at this Code Sandbox and see how it works.
Here's how I added styled to my chart:
function MyChart({}) {
const { data } = useChartConfig({
height: 200,
grouping: "primary",
dataType: "ordinal",
});
const getSeriesStyle = React.useCallback((series) => {
// Based off my chart bars
const colorPalette = {
series1: "#354657",
series2: "#5597e2",
series3: "#28A96C",
series4: "#d44401",
series5: "#ffe65b",
series6: "#ffab1b",
};
return {
fill: colorPalette[series.label],
};
}, []);
return <Chart data={data} getSeriesStyle={getSeriesStyle} />;
}
As I am new to react i have been struggling to pass data from my state to the chartjs dynamically...what I need is when ever user updates the Textbox and asks for the Output, the Pie chart should update itself automatically according to the output...i have stored the output values in the state but Pie chart is not allowing me to Pass state as data...
Here's a Link to the Code Sandbox
https://codesandbox.io/s/autumn-mountain-sv1qk?fontsize=14&hidenavigation=1&theme=dark
class Buyer extends Component {
constructor(props) {
super(props);
this.state = {
income: 100000,
percent: 13,
totalTax: '',
grossPrice: '',
otherValues: '',
}
}
PieChart = (Gross,totalTax) => {
let GrossPrice = Gross ;
let TotalTax = totalTax ;
let data = [GrossPrice,TotalTax]
let Pie = {
labels: ['Total Tax', 'Gross Price'],
datasets: [{
data: data,
backgroundColor: [
'#1ca5b6',
'#89ba2b',
],
}]
}
}
handleIncome = (event) => {
let income = event.target.value
this.handleData(income, this.state.percent)
console.log(this.state)
}
handlePercent = (event) => {
let Percent = event.target.value
this.handleSliderData(this.state.income, Percent)
}
// From Slider
sliderIncome = (event) => {
this.setState({ income: event })
this.handleSliderData(event, this.state.percent)
// console.log(this.state)
}
sliderPercent = (event) => {
this.setState({ percent: event })
this.handleSliderData(this.state.income, event)
}
handleData = (income, Percent) => {
this.setState({
income: income,
percent: Percent,
totalTax: //some Calculations
grossPrice: //some Calculations
otherValues: //some Calculations
})
console.log(this.state)
}
handleSliderData = (income, Percent) => {
this.setState({
income: income,
percent: Percent,
totalTax: //some Calculations
grossPrice://some Calculations
otherValues://some Calculations
})
this.PieChart(this.state.grossPrice,this.state.totalTax)
// console.log(this.state)
}
render() {
return (
<div>
<div >
<Card s>
<PieChart data={this.PieChart} width={600} height={300} />
</Card>
</div>
</Col>
)
}
I have tried creating a function for the pie chart but was not able to get through...any help would be appreciated..thanks!!
I think there are a few problems with the code.
this.PieChart function doesn't return anything now. From giving the code a quick glance, I can see that you are trying to pass the props needed for the PieChart component from this.PieChart function. Return whatever you need as prop for the component and also call the function inside the JSX using parenthesis, passing the needed parameters into the function.
PieChart = (Gross,totalTax) => {
let GrossPrice = Gross ;
let TotalTax = totalTax ;
let data = [GrossPrice,TotalTax]
let Pie = {
labels: ['Total Tax', 'Gross Price'],
datasets: [{
data: data,
backgroundColor: [
'#1ca5b6',
'#89ba2b',
],
}]
}
}
return Pie; //or whatever data you need for the component
Also,
<PieChart data={this.PieChart(this.state.grossPrice, this.state.totalTax)} width={600} height={300} />
Also, keep in mind to use proper naming conventions. Functions should be in camel case( this.PieChart should be named this.pieChart ). Try using different names for the component and function. This shall solve a lot of problems you might run into
Update: I have updated the sandbox you have shared. Hope this helps.
https://codesandbox.io/s/friendly-mahavira-79n2s
I think i found the solution for the particular problem...i order to update the data in the piechart dynamically using state we have create a state in the constructor(props) like so...
constructor(props) {
super(props);
this.state = {
income: '',
percent: '',
totalTax: '',
grossPrice: '',
otherValues: '',
Data: {}
}
}
Then i have used componentDidMount() to mount the initial state of the PieChart like So....
componentDidMount() {
let runscore = [100,200];
this.setState(
{
Data: {
labels: [
'Purple',
'Yellow',
],
datasets: [
{
data: runscore,
backgroundColor: [
'#1ca5b6',
'#89ba2b',
]
}
]
}
});
}
then i created a function PieChart = (Gross,totalTax) that fetches data from another functions and used that to set state in Data like so...
PieChart = (Gross,totalTax) => {
let runscore = [Gross,totalTax];
this.setState({
Data: {
labels: [
'Gross Price',
'Total Tax',
],
datasets: [
{
data: runscore,
backgroundColor: [
'#1ca5b6',
'#89ba2b',
]
}
]
}
});
}
For now this is not updating the state in sync with the current state but i gets the work done...i hope this helps others...
I am using react js and trying to send Props from my app.js to chart.js file. When I send the hardcoded values the values are being send properly and graph is made according to it. But whenever I am passing a dynamic values, values are being passed but not used by chart.
App.js
class App extends React.Component {
state = {
text: "",
credit: [{_id:1,financeCredit:"loading"}],
debit: [{_id:1,financeDebit:"loading"}],
}
componentDidMount(){
fetch('/data')
.then(res=> res.json())
.then(res2 => {
console.log(res2)
this.setState({
credit: res2
})
})
fetch('/data2')
.then(res=> res.json())
.then(res2 => {
console.log(res2)
this.setState({
debit: res2
})
})
}
render(){
var lengthExpense = this.state.credit.length;
console.log(lengthExpense)
var expName = [];
for (var a = 0 ; a <= lengthExpense ; a++){
if (this.state.credit[a]){
expName.push(this.state.credit[a].expenseName)
}
}
var expAmount = [];
for (var b = 0 ; b <= lengthExpense ; b++){
if(this.state.credit[b])
expAmount.push(this.state.credit[b].expenseAmount)
}
console.log(expAmount)
console.log(expName)
return (
<BrowserRouter>
<div className="App">
<Navbar />
<Chart expam = {expAmount} expnam = {expName} />
<Route exact path = "/" component = {Home} />
</div>
</BrowserRouter>
);
}
}
export default App;
Although the following console.log() is showing the desired values I want to pass
console.log(expAmount)
console.log(expName)
I am passing these values like this
<Chart expam = {expAmount} expnam = {expName} />
In chart.js although I am getting these values.
Chart.js
class Chart extends Component{
constructor(props){
super(props);
this.state = {
chartData : {
labels: this.props.expnam,
datasets: [{
label: 'Expense',
data: this.props.expam,
backgroundColor:'rgba(255, 99, 132, 0.6)'
}]
}
}
}
render(){
console.log(this.props)
return(
<div className = "chart">
<div>
<Bar id = "chart1"
data={this.state.chartData}
options={{ maintainAspectRatio: false, }}
/>
<canvas id = "chart1" height="30vw" width="10vw" ></canvas>
</div>
</div>
)
}
}
But couldn't able to pass it to to labels and data. Code is properly running but there is no values so chart is being showed empty
constructor(props){
super(props);
this.state = {
chartData : {
labels: this.props.expnam,
datasets: [{
label: 'Expense',
data: this.props.expam,
backgroundColor:'rgba(255, 99, 132, 0.6)'
}]
}
}
}
In this chart.js file I can see all the values that are being passed from App.js. But these values are not been used for chart (bar chart).
console.log(this.props)
It seems like you're setting the data in your constructor:
constructor(props){
super(props);
this.state = {
chartData : {...}
}
}
A constructor is only called once when an object is initialized. (In ReactJS, it is only called after the first render.)
You are calling setState(), and the rest appears to be good, that I can tell. Why don't you move this.state = {...} from the constructor to render()? Since render() runs whenever the state is changed, your setState() calls should work.
It may be inelegant, and there can certainly be improvements, but it will get you started in the right direction.
I'm trying to display a barchart using React-Chartkick and Chart.js, and I'd like to customise the colours of the bars. Currently, I'm able to set all the bars to the same colour by passing a prop like this: <BarChart colours={["#fff"]} />.
Using LineCharts in React-Chartkick, you can set colours of the lines by passing an array of colours through that prop. BarCharts only seems to accept the first colour, however. Is this a limitation of React-Chartkick, or am I doing something wrong?
I've tried passing options (as described here: https://www.chartjs.org/docs/latest/charts/bar.html#styling ) through the library prop as that is how I've customised the colours of the axes and labels, but this doesn't seem to affect the bars.
Here's my current code:
state = {
chartLibraryOptions: {
borderColor: "#e34402", // does nothing here
backgroundColor: "#e34402", // nor this
scales: {
yAxes: [
{
ticks: { fontColor: "#fff", autoSkip: false }
}
],
xAxes: [
{
ticks: { fontColor: "#fff" }
}
]
}
}
};
render() {
return (
<BarChart
data={this.state.chartData}
library={this.state.chartLibraryOptions}
colors={["#e34402", "#e3b502"]} // All bars are the first colour
/>
);
}
I'm expecting to be able to change the colours of each bar, but after all this I'm not sure if that's possible through Chartkick?
Well, I used the same node package in a project with different approach kinda work for me. Almost all the charts take the same attributes.
Basically, this attribute dataset={{ backgroundColor: ['white', 'yellow' ], }}
is all you need to colour each bar. You can either pass string or array of string to backgroundColor.
The backgroundColor in dataset takes two types of data String and Array(object). Typical examples of passing data are below.
When you set backgroundColor to a string, it applied the same colour to each bar. e.g backgroundColor: 'red'
BarChart - <BarChart dataset={{ backgroundColor: 'red', }} />
When you set backgroundColor to an array, it applied each colour in the array to each bar. e.g backgroundColor: ['red', 'yellow'], then you create a loop of colours base on the data length.
column chart - <ColumnChart dataset={{ backgroundColor: ['red', 'yellow' ], }} />
React Implementation Below:
/* eslint-disable no-plusplus */
import React from 'react';
import { ColumnChart, BarChart } from 'react-chartkick';
import { chartOne } from '../common/chartData';
import 'chart.js';
const MonthlyGrowth = () => {
const handleBgColors = () => {
const firstColor = "#A00B16", secondColor = "#FAA226";
const arrOfBgColors = [];
for (let i = 1; i <= chartOne.length; i++) {
if (i % 2 === 0) {
arrOfBgColors.push(secondColor)
} else {arrOfBgColors.push(firstColor)}
}
return arrOfBgColors;
}
return (
<div className="bukka-card uk-card-default bg-white pt-4 pb-2 mr-1 pl-3 pr-2 pl-
lg-5">
<h2 className="mt-2">4,500,000</h2>
<BarChart
dataset={{ borderWidth: 0, width: 0, backgroundColor: handleBgColors(), }}
data={chartOne}
/>
</div>
)
}
export default MonthlyGrowth;
I am using highcharts-react-official in react-redux to create a drilldown chart.
However when I click on a bar to drilldown, I also update some props which causes the component to re-render - which seems to prevent the drilldown event.
I kind of gathered from Change series data dynamically in react-highcharts without re-render of the chart that I should use shouldComponentUpdate and getChart() to prevent re-render and instead dynamically update the data.
My issue is that getChart() doesn't seem to work for the official highcharts react package. I'm getting Uncaught TypeError: this.refs.chart.getChart is not a function
Is there an alternative I'm meant to be using to get and dynamically update the chart? Or some examples that I could look at?
Just including render and shouldComponentUpdate parts here:
shouldComponentUpdate(nextProps, nextState) {
let chart = this.refs.chart.getChart();
//dynamically update data using nextProps
return false;
}
render () {
const options = {
chart: {
type: 'column',
height: 300,
events: {
drillup: (e) => {this.drilledUp(e)}
}
},
plotOptions: {
series: {
events:{
click: (e) => {this.categoryClicked(e)}
}
}
},
xAxis: {type: "category"},
yAxis: {title: {text: 'Amount Spent ($)'}},
series: [{
name: 'weekly spending',
showInLegend: false,
data: this.props.transactionChartData.series_data,
cursor: 'pointer',
events: {
click: (e)=> {this.weekSelected(e)}
}
}],
drilldown: {
series: this.props.transactionChartData.drilldown_data
}
};
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
ref="chart"
/>
)
}
In highcharts-react-official v2.0.0 has been added allowChartUpdate option, which should work great in your case. By using this option you can block updating the chart with updating the component:
categoryClicked() {
this.allowChartUpdate = false;
this.setState({
...
});
}
...
<HighchartsReact
ref={"chartComponent"}
allowChartUpdate={this.allowChartUpdate}
highcharts={Highcharts}
options={...}
/>
Moreover, to get the chart instance use refs:
componentDidMount(){
const chart = this.refs.chartComponent.chart;
}
Live demo: https://codesandbox.io/s/98nl4pp5r4
// react native functional
import React, {useState, useRef,useLayoutEffect} from "react"
import HighchartsReactNative from '#highcharts/highcharts-react-native'
function chartcomponent(props){
const [options, setoptions] = useState({});
const chartRef = useRef(null);
useEffect(() => {
// create the options for your chart.
setoptions({chart:{}, yaxis:{}, xAxis:{},})// etc.
}, []);
useLayoutEffect(() => {
// new point to add, you can get new data via props, fetch, socket, etc.
var x = new Date(hr.timestamp).getTime();
var y = 10
// these are the important parts here:
var series = chartRef.current.props.options.series;
if (!Array.isArray(series)){return;}
var seriesdata = series[0].data;
seriesdata.push([x,y]);
// next line limits points in chart to 10, so new poitn replaces first point
if (seriesdata.length>10){seriesdata.splice(0,1);}
return () => {};
}, [props]);
return(
<View style={styles.charting}><HighchartsReactNative styles={{height:300, width:600}} options={options} ref={chartRef} ></HighchartsReactNative></View>
)
}