我们可以将 reactJS 与 amCharts 一起使用吗
Can we use reactJS with amCharts
想知道将 reactJS 与 amCharts 图表库一起使用在技术上是否可行。如果是,是否有人尝试过或有我可以参考的演示。另外,amCharts 的渲染体验是否会因为使用 reactJS 而有所改善?
是的,您基本上需要制作一个包装器组件并连接库的初始化。配置通常最好作为 props 传递给组件。
React 擅长渲染 React 组件。任何由非 React 库渲染的东西对于 React 来说就像一个黑盒子,你看不到任何好处。事实上,如果你没有很好地编写你的包装器组件并继续重新初始化或更新库,即使配置没有改变,你最终可能会有很大的开销。
我们在我正在处理的项目中使用 highcharts 作为柱形图。该代码不是开源的,而且太大太复杂,无法用作简单的示例,但这是它的基本结构:
import React from 'react';
import Highcharts from 'highcharts';
export default React.createClass({
displayName: 'BarChart',
componentDidMount: function () {
this._initChart();
},
shouldComponentUpdate: function (nextProps) {
return nextProps.chartData != this.props.chartData || nextProps.emptyMessage != this.props.emptyMessage;
},
componentDidUpdate: function () {
this._initChart();
},
_initChart: function () {
//do not attempt to render if there's no data, insert placeholder instead
//since it's a jquery plugin we also render the placeholder with jquery to keep React out of it
if (!this.props.chartData || this.props.chartData.length == 0) {
var jqContainer = $(this.refs.container);
var height = jqContainer.height() + 'px';
jqContainer.html($('<span class="bar-chart-empty-placeholder"><i class="' +
((this.props.emptyMessage.indexOf('Loading') > -1) ? 'fa fa-spinner fa-pulse' : '') + '"></i> ' +
this.props.emptyMessage + '</span>').css({'height': height, 'line-height': height}));
return;
}
//data transformation
//some logic here, transforming data from API to format expected by highcharts
$(this.refs.container).highcharts({
chart: {
type: 'column'
},
exporting: {
enabled: false
},
legend: {
enabled: false
},
credits: {
enabled: false
},
title: {
text: ''
},
xAxis: {
tickWidth: 0,
tickPixelInterval: 130,
type: 'datetime',
labels: {
formatter: function () {
return dateFormatter(this.value, dateGrain);
}
}
},
yAxis: {
min: '0',
labels: {
format: '{value} %'
},
},
plotOptions: {
column: {
stacking: 'percent',
groupPadding: 0,
pointPadding: 0,
borderWidth: 0,
dataLabels: {
enabled: false,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
}
}
}
},
series: seriesArray
});
},
render: function () {
//output the title and the container div, contents will be rendered by highcharts
return (
<div className="chart-legend clearfix">
<h4 className="pull-left">{this.props.title}</h4>
<div ref="container"
style={{height: '300px', paddingLeft: 0, marginTop: '50px', marginBottom: '30px'}}
></div>
</div>
);
}
});
为了进一步优化,如果 title 永远不会改变,我们可以在 props 改变时调用 shouldComponentUpdate 中的 _initChart 并使其始终 return true 以便容器 html 永远不会被 React 重新渲染。
Amchart 使用 ReactJs Demo(饼图)
import React from "react";
import AmCharts from "@amcharts/amcharts3-react";
import { graphType } from "../Graph/GraphType";
import { toolTipType } from "../Graph/ToolTipType";
class PieGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
dataProvider: JSON.parse(props.dataSet.Data)
};
}
render() {
// Render the chart
var graphProperty = this.props.dataSet;
var controlId=this.props.controlKey;
var legendId="legendDiv_"+controlId;
var chartType = graphProperty.GraphType;
var is3DChart = graphProperty.Is3DChart;
var toolTipFormat = graphProperty.ToolTipFormat;
var isLegendRequired = graphProperty.IsLegendRequired;
var subTitle = graphProperty.SubTitle;
var radious="";
var innerRadius="";
if (chartType == graphType.Doughnut) {
radious = "30%";
innerRadius = "35%";
}
var title=[{
text : subTitle,
id: "Title-1"
//size: 15
}];
var depth3D="";
var angle="";
if (is3DChart)
{
depth3D = 8;
angle = 20;
}
var labelText= "[[percents]]%([[value]])";
var balloonText= "[[sliceText]]<br><span style='font-size:14px'><b>[[value]]</b> ([[percents]]%)</span>";
if (toolTipFormat == "None" || toolTipFormat == toolTipType.None) {
labelText = "[[value]]";
balloonText = "";
}
else if (toolTipFormat == "ShowValue" || toolTipFormat == toolTipType.ShowValue) {
labelText = "[[value]]";
balloonText = "[[sliceText]]<br><span style='font-size:14px'><b>[[value]]</b></span>";
}
else if (toolTipFormat == "ShowPercentage" || toolTipFormat == toolTipType.ShowPercentage) {
labelText = "[[percents]]%";
balloonText = "[[sliceText]]<br><span style='font-size:14px'>([[percents]]%)</b></span>";
}
var marginTop=10;
if(subTitle== "")
marginTop = -50;
var legend="";
var isfromRportDetail=1;//Temporary assume true....
if(isLegendRequired)
{
var legendEqualWidths=false;
var lagendPosition="";
var labelWidth="";
var legendMarginLeft="";
var legendalign="";
if (isfromRportDetail) { //added this condition for reportdetail charts for else in running other report chart
lagendPosition = "right";
legendMarginLeft = 0;
legendEqualWidths = true;
legendalign="right";
}
else {
legendalign = "center";
legendEqualWidths = false;
labelWidth = 1;
}
legend=[{
divId:legendId,
color : "#999999",
markerSize : 3,
markerBorderThickness : 4,
valueText : "",
truncateLabels : 15,
position :lagendPosition,
marginLeft : legendMarginLeft,
equalWidths :labelWidth,
align : legendalign,
labelWidth : legendEqualWidths,
markerType: "circle",
listeners: [{
event: "clickSlice",
method: function (event) {
if (event.dataItem.dataContext.url != undefined) {
if (event.dataItem.dataContext.url != "")
popdlgrs(event.dataItem.dataContext.url, "_blank", 800, 650);
}
else {
return "";
}
}
}]
}];
}
return (
<AmCharts.React
type="pie"
theme="light"
startAngle="355"
color="#999999"
labelRadius="20"
titles={title}
pullOutRadius="12%"
precision="2"
percentPrecision="2"
thousandsSeparator=","
decimalSeparator="."
marginBottom="10"
marginTop={marginTop}
radius={radious}
depth3D = {depth3D}
angle = {angle}
innerRadius={innerRadius}
dataProvider={this.state.dataProvider}
valueField={graphProperty.ChartDataFields}
titleField={graphProperty.ChartCetegoryFields}
labelText = {labelText}
balloonText = {balloonText}
colors = {["#ED807D", "#F0BF73", "#B5D766", "#9BDDDB", "#8EC5E4", "#D094DC", "#CDDC39", "#3396da", "#b85acc", "#60b2ae", "#85ae21", "#eda22d", "#e54c4f", "#D66C6E", "#8BC24A", "#70D5C7", "#ED807D", "#F0BF73", "#B5D766", "#9BDDDB", "#8EC5E4", "#D094DC", "#CDDC39", "#3396da", "#b85acc", "#60b2ae", "#85ae21", "#eda22d", "#e54c4f", "#D66C6E", "#8BC24A", "#70D5C7"]}
legend={legend}
balloon={[{
fixedPosition: "true",
drop: "true"
}]}
listeners={
[{
event: "clickSlice",
method: function (event)
{
if (event.dataItem.dataContext.url != undefined) {
if (event.dataItem.dataContext.url != "")
popdlgrs(event.dataItem.dataContext.url, "_blank", 800, 650);
}
else {
return "";
}
}
}]
}
/>);
}
}
export default PieGraph;
想知道将 reactJS 与 amCharts 图表库一起使用在技术上是否可行。如果是,是否有人尝试过或有我可以参考的演示。另外,amCharts 的渲染体验是否会因为使用 reactJS 而有所改善?
是的,您基本上需要制作一个包装器组件并连接库的初始化。配置通常最好作为 props 传递给组件。
React 擅长渲染 React 组件。任何由非 React 库渲染的东西对于 React 来说就像一个黑盒子,你看不到任何好处。事实上,如果你没有很好地编写你的包装器组件并继续重新初始化或更新库,即使配置没有改变,你最终可能会有很大的开销。
我们在我正在处理的项目中使用 highcharts 作为柱形图。该代码不是开源的,而且太大太复杂,无法用作简单的示例,但这是它的基本结构:
import React from 'react';
import Highcharts from 'highcharts';
export default React.createClass({
displayName: 'BarChart',
componentDidMount: function () {
this._initChart();
},
shouldComponentUpdate: function (nextProps) {
return nextProps.chartData != this.props.chartData || nextProps.emptyMessage != this.props.emptyMessage;
},
componentDidUpdate: function () {
this._initChart();
},
_initChart: function () {
//do not attempt to render if there's no data, insert placeholder instead
//since it's a jquery plugin we also render the placeholder with jquery to keep React out of it
if (!this.props.chartData || this.props.chartData.length == 0) {
var jqContainer = $(this.refs.container);
var height = jqContainer.height() + 'px';
jqContainer.html($('<span class="bar-chart-empty-placeholder"><i class="' +
((this.props.emptyMessage.indexOf('Loading') > -1) ? 'fa fa-spinner fa-pulse' : '') + '"></i> ' +
this.props.emptyMessage + '</span>').css({'height': height, 'line-height': height}));
return;
}
//data transformation
//some logic here, transforming data from API to format expected by highcharts
$(this.refs.container).highcharts({
chart: {
type: 'column'
},
exporting: {
enabled: false
},
legend: {
enabled: false
},
credits: {
enabled: false
},
title: {
text: ''
},
xAxis: {
tickWidth: 0,
tickPixelInterval: 130,
type: 'datetime',
labels: {
formatter: function () {
return dateFormatter(this.value, dateGrain);
}
}
},
yAxis: {
min: '0',
labels: {
format: '{value} %'
},
},
plotOptions: {
column: {
stacking: 'percent',
groupPadding: 0,
pointPadding: 0,
borderWidth: 0,
dataLabels: {
enabled: false,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
}
}
}
},
series: seriesArray
});
},
render: function () {
//output the title and the container div, contents will be rendered by highcharts
return (
<div className="chart-legend clearfix">
<h4 className="pull-left">{this.props.title}</h4>
<div ref="container"
style={{height: '300px', paddingLeft: 0, marginTop: '50px', marginBottom: '30px'}}
></div>
</div>
);
}
});
为了进一步优化,如果 title 永远不会改变,我们可以在 props 改变时调用 shouldComponentUpdate 中的 _initChart 并使其始终 return true 以便容器 html 永远不会被 React 重新渲染。
Amchart 使用 ReactJs Demo(饼图)
import React from "react";
import AmCharts from "@amcharts/amcharts3-react";
import { graphType } from "../Graph/GraphType";
import { toolTipType } from "../Graph/ToolTipType";
class PieGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
dataProvider: JSON.parse(props.dataSet.Data)
};
}
render() {
// Render the chart
var graphProperty = this.props.dataSet;
var controlId=this.props.controlKey;
var legendId="legendDiv_"+controlId;
var chartType = graphProperty.GraphType;
var is3DChart = graphProperty.Is3DChart;
var toolTipFormat = graphProperty.ToolTipFormat;
var isLegendRequired = graphProperty.IsLegendRequired;
var subTitle = graphProperty.SubTitle;
var radious="";
var innerRadius="";
if (chartType == graphType.Doughnut) {
radious = "30%";
innerRadius = "35%";
}
var title=[{
text : subTitle,
id: "Title-1"
//size: 15
}];
var depth3D="";
var angle="";
if (is3DChart)
{
depth3D = 8;
angle = 20;
}
var labelText= "[[percents]]%([[value]])";
var balloonText= "[[sliceText]]<br><span style='font-size:14px'><b>[[value]]</b> ([[percents]]%)</span>";
if (toolTipFormat == "None" || toolTipFormat == toolTipType.None) {
labelText = "[[value]]";
balloonText = "";
}
else if (toolTipFormat == "ShowValue" || toolTipFormat == toolTipType.ShowValue) {
labelText = "[[value]]";
balloonText = "[[sliceText]]<br><span style='font-size:14px'><b>[[value]]</b></span>";
}
else if (toolTipFormat == "ShowPercentage" || toolTipFormat == toolTipType.ShowPercentage) {
labelText = "[[percents]]%";
balloonText = "[[sliceText]]<br><span style='font-size:14px'>([[percents]]%)</b></span>";
}
var marginTop=10;
if(subTitle== "")
marginTop = -50;
var legend="";
var isfromRportDetail=1;//Temporary assume true....
if(isLegendRequired)
{
var legendEqualWidths=false;
var lagendPosition="";
var labelWidth="";
var legendMarginLeft="";
var legendalign="";
if (isfromRportDetail) { //added this condition for reportdetail charts for else in running other report chart
lagendPosition = "right";
legendMarginLeft = 0;
legendEqualWidths = true;
legendalign="right";
}
else {
legendalign = "center";
legendEqualWidths = false;
labelWidth = 1;
}
legend=[{
divId:legendId,
color : "#999999",
markerSize : 3,
markerBorderThickness : 4,
valueText : "",
truncateLabels : 15,
position :lagendPosition,
marginLeft : legendMarginLeft,
equalWidths :labelWidth,
align : legendalign,
labelWidth : legendEqualWidths,
markerType: "circle",
listeners: [{
event: "clickSlice",
method: function (event) {
if (event.dataItem.dataContext.url != undefined) {
if (event.dataItem.dataContext.url != "")
popdlgrs(event.dataItem.dataContext.url, "_blank", 800, 650);
}
else {
return "";
}
}
}]
}];
}
return (
<AmCharts.React
type="pie"
theme="light"
startAngle="355"
color="#999999"
labelRadius="20"
titles={title}
pullOutRadius="12%"
precision="2"
percentPrecision="2"
thousandsSeparator=","
decimalSeparator="."
marginBottom="10"
marginTop={marginTop}
radius={radious}
depth3D = {depth3D}
angle = {angle}
innerRadius={innerRadius}
dataProvider={this.state.dataProvider}
valueField={graphProperty.ChartDataFields}
titleField={graphProperty.ChartCetegoryFields}
labelText = {labelText}
balloonText = {balloonText}
colors = {["#ED807D", "#F0BF73", "#B5D766", "#9BDDDB", "#8EC5E4", "#D094DC", "#CDDC39", "#3396da", "#b85acc", "#60b2ae", "#85ae21", "#eda22d", "#e54c4f", "#D66C6E", "#8BC24A", "#70D5C7", "#ED807D", "#F0BF73", "#B5D766", "#9BDDDB", "#8EC5E4", "#D094DC", "#CDDC39", "#3396da", "#b85acc", "#60b2ae", "#85ae21", "#eda22d", "#e54c4f", "#D66C6E", "#8BC24A", "#70D5C7"]}
legend={legend}
balloon={[{
fixedPosition: "true",
drop: "true"
}]}
listeners={
[{
event: "clickSlice",
method: function (event)
{
if (event.dataItem.dataContext.url != undefined) {
if (event.dataItem.dataContext.url != "")
popdlgrs(event.dataItem.dataContext.url, "_blank", 800, 650);
}
else {
return "";
}
}
}]
}
/>);
}
}
export default PieGraph;