我正在使用我的第一个React应用程序,该应用程序根据用户在下拉菜单中的当前选择显示Chart.js图表。我在它改变选择时可以渲染相应图表的那一点,但它使用的是硬编码数据。从那时起我就添加了一个Ajax调用来获取JSON,但是在Ajax响应进来之前,图表组件已经呈现。所以,我添加了一个检查来显示"正在加载......"等待JSON数据在状态中设置。我的问题是,我呈现正确图表的当前实现与我在渲染图表之前等待JSON数据的实现相冲突。
经过研究,我发现我的问题与这篇文章最相似,但有多种图表可能性,而不是一个:React render Chart.js based on api response
这是我的代码:
App.js
import React, { Component } from 'react';
import $ from 'jquery';
import './App.css';
import Header from './Components/Header';
import Dropdown from './Components/Dropdown';
import BarChart from './Components/BarChart';
import DoughnutChart from './Components/DoughnutChart';
import PieChart from './Components/PieChart';
import LineChart from './Components/LineChart';
import RadarChart from './Components/RadarChart';
class App extends Component {
constructor(props) {
super(props);
this.state = {
chartData: {},
chartType: 'Bar', //default to bar chart
isLoaded: false
};
this.handleVisualChange = this.handleVisualChange.bind(this);
}
componentDidMount() {
this.getChartData();
}
handleVisualChange(value) {
//logging for testing
console.log('testing value in App.js: ', value);
//updating the state to the current selected visual
this.setState({chartType: value});
}
getChartData() {
$.ajax({
url: 'http://localhost/fetch/getData.php',
dataType: 'json',
success: function(dataReturned){
//logging to test if correct data is being received
console.log('original data: ', dataReturned);
//loop through array and build array of categories and array of totals
var arrCAT = [];
var arrTOTAL = [];
for (var i = 0, len = dataReturned.length; i < len; i++) {
arrCAT.push(dataReturned[i].CAT);
arrTOTAL.push(dataReturned[i].TOTAL);
}
//logging to test that arrays were loaded correctly
console.log('just categories: ', arrCAT);
console.log('just totals: ', arrTOTAL);
this.setState({
chartData: {
labels: arrCAT,
datasets: [
{
label: '# of Transactions',
data: arrTOTAL,
backgroundColor: [
'rgba(255, 99, 132, 0.6)', //red
'rgba(54, 162, 235, 0.6)', //blue
'rgba(255, 206, 86, 0.6)', //yellow
'rgba(75, 192, 192, 0.6)', //green
'rgba(153, 102, 255, 0.6)', //purple
'rgba(255, 159, 64, 0.6)', //orange
'rgba(90, 96, 104, 0.6)' //grey
]
}
]
},
isLoaded: true
});
}.bind(this),
});
}
render() {
let chartToBeDisplayed = null;
switch(this.state.chartType) {
case 'Bar':
chartToBeDisplayed = <BarChart chartData={this.state.chartData} />;
break;
case 'Doughnut':
chartToBeDisplayed = <DoughnutChart chartData={this.state.chartData} />;
break;
case 'Pie':
chartToBeDisplayed = <PieChart chartData={this.state.chartData} />;
break;
case 'Line':
chartToBeDisplayed = <LineChart chartData={this.state.chartData} />;
break;
case 'Radar':
chartToBeDisplayed = <RadarChart chartData={this.state.chartData} />;
break;
case 'All':
chartToBeDisplayed = <div className="all">
<BarChart chartData={this.state.chartData} />
<DoughnutChart chartData={this.state.chartData} />
<PieChart chartData={this.state.chartData} />
<LineChart chartData={this.state.chartData} />
<RadarChart chartData={this.state.chartData} />
</div>;
break;
default:
console.log('Something went wrong...');
}
return (
<div className="App">
<Header />
<Dropdown onVisualChange={this.handleVisualChange} />
<div className="ChartArea">
{this.state.isLoaded ? {chartToBeDisplayed} : <div>Loading...</div>}
</div>
</div>
);
}
}
export default App;
Dropdown.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Dropdown extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
static defaultProps = {
visuals: ['Bar', 'Line', 'Pie', 'Doughnut', 'Radar', 'All']
}
handleChange(event) {
//logging for testing
console.log('testing value in Dropdown.js: ', event.target.value);
this.props.onVisualChange(event.target.value);
}
render() {
let visualOptions = this.props.visuals.map(visual => {
return <option key={visual} value={visual}>{visual}</option>
});
return (
<div className="dropdown">
<label>Visuals</label><br />
<select id="soflow" ref="visual" value={this.props.value} onChange={this.handleChange}>
{visualOptions}
</select>
</div>
);
}
}
Dropdown.propTypes = {
onVisualChange: PropTypes.func,
visuals: PropTypes.array,
value: PropTypes.string
};
export default Dropdown;
BarChart.js (除图表类型外,其他图表组件相同)
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Bar} from 'react-chartjs-2';
class BarChart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData
}
}
static defaultProps = {
displayTitle: true,
displayLegend: true,
legendPosition: 'bottom',
}
render() {
return (
<div className="chart">
<Bar
data={this.state.chartData}
options={{
title: {
display: this.props.displayTitle,
text: 'Transactions by Category',
fontSize: 25
},
legend: {
display: this.props.displayLegend,
position: this.props.legendPosition
}
}}
/>
</div>
)
}
}
BarChart.propTypes = {
chartData: PropTypes.object,
displayTitle: PropTypes.bool,
displayLegend: PropTypes.bool,
legendPosition: PropTypes.string
};
export default BarChart;
当前错误:
Objects are not valid as a React child (found: object with keys {chartToBeDisplayed}). If you meant to render a collection of children, use an array instead.
in div (at App.js:112)
in div (at App.js:109)
in App (at index.js:7)
在App.js中,我知道{this.state.isLoaded ? {chartToBeDisplayed} : <div>Loading...</div>}
是不正确的,但这是我第一次尝试合并我拥有的两种技术。我的第二次尝试是将{chartToBeDisplayed}
替换为通用图表组件(<Chart />
),并将switch(this.state.chartType)
置于该组件的render()
内,如下所示:
chart.js之
import React, {Component} from 'react';
import BarChart from './BarChart';
import DoughnutChart from './DoughnutChart';
import PieChart from './PieChart';
import LineChart from './LineChart';
import RadarChart from './RadarChart';
import PropTypes from 'prop-types';
import {Bar, Line, Pie, Doughnut, Radar} from 'react-chartjs-2';
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData,
chartType: props.chartType
}
}
render() {
let chartToBeDisplayed = null;
switch(this.state.chartType) {
case 'Bar':
chartToBeDisplayed = <BarChart chartData={this.state.chartData} />;
break;
case 'Doughnut':
chartToBeDisplayed = <DoughnutChart chartData={this.state.chartData} />;
break;
case 'Pie':
chartToBeDisplayed = <PieChart chartData={this.state.chartData} />;
break;
case 'Line':
chartToBeDisplayed = <LineChart chartData={this.state.chartData} />;
break;
case 'Radar':
chartToBeDisplayed = <RadarChart chartData={this.state.chartData} />;
break;
case 'All':
chartToBeDisplayed = <div className="all">
<BarChart chartData={this.state.chartData} />
<DoughnutChart chartData={this.state.chartData} />
<PieChart chartData={this.state.chartData} />
<LineChart chartData={this.state.chartData} />
<RadarChart chartData={this.state.chartData} />
</div>;
break;
default:
console.log('Something went wrong...');
}
return (
<div className="chart">
{chartToBeDisplayed}
</div>
);
}
}
Chart.propTypes = {
chartData: PropTypes.object,
chartType: PropTypes.string,
};
export default Chart;
通过此实现,它会将 App.js 更改为:
import React, { Component } from 'react';
import $ from 'jquery';
import './App.css';
import Header from './Components/Header';
import Dropdown from './Components/Dropdown';
import Chart from './Components/Chart';
class App extends Component {
constructor(props) {
super(props);
this.state = {
chartData: {},
chartType: 'Bar',
isLoaded: false
};
this.handleVisualChange = this.handleVisualChange.bind(this);
}
componentDidMount() {
this.getChartData();
}
handleVisualChange(userSelection) {
//logging for testing
console.log('testing value in App.js: ', userSelection);
//updating the state to the current selected visual
this.setState({chartType: userSelection});
}
getChartData() {
$.ajax({
url: 'http://localhost/test/fetch.php',
dataType: 'json',
success: function(dataReturned){
//logging to test if correct data is being received
console.log('original data: ', dataReturned);
//loop through array and build array of categories and array of totals
var arrCAT = [];
var arrTOTAL = [];
for (var i = 0, len = dataReturned.length; i < len; i++) {
arrCAT.push(dataReturned[i].CAT);
arrTOTAL.push(dataReturned[i].TOTAL);
}
//logging to test that arrays were loaded correctly
console.log('just categories: ', arrCAT);
console.log('just totals: ', arrTOTAL);
this.setState({
chartData: {
labels: arrCAT,
datasets: [
{
label: '# of Transactions',
data: arrTOTAL,
backgroundColor: [
'rgba(255, 99, 132, 0.6)', //red
'rgba(54, 162, 235, 0.6)', //blue
'rgba(255, 206, 86, 0.6)', //yellow
'rgba(75, 192, 192, 0.6)', //green
'rgba(153, 102, 255, 0.6)', //purple
'rgba(255, 159, 64, 0.6)', //orange
'rgba(90, 96, 104, 0.6)' //grey
]
}
]
},
isLoaded: true
});
}.bind(this),
});
}
render() {
return (
<div className="App">
<Header />
<Dropdown onVisualChange={this.handleVisualChange} />
<div className="ChartArea">
{this.state.isLoaded ? <Chart chartData={this.state.chartData} chartType={this.state.chartType} /> : <div>Loading...</div>}
</div>
</div>
);
}
}
export default App;
但是,我也无法让它发挥作用。当我在下拉列表中更改选择时,React Developer Tools允许我看到状态正在正确地更改,但它不会呈现新的图表选择。它只是保持默认值,即BarChart。
结论:我不确定我是否接近实现这一点,但是我缺少一些细节,或者我是否需要对如何呈现正确的图表进行重大更改? switch语句实现感觉不对,但它本身工作得很好。正如我所说,我是新手与React合作,并希望得到一些方向/澄清。谢谢!
答案 0 :(得分:0)
首次试用时,请尝试在curly brackets
删除chartToBeDisplayed
。
{this.state.isLoaded ? chartToBeDisplayed : <div>Loading...</div>}
只要图表的语法正确,它就应该有效。
进行第二次试验。
Chart.js
仅在初始安装时设置状态。
因此,当您在App.js
上更新状态并将新道具 - chartType
传递给Chart.js
时,您需要update the local state
。
尝试在componentWillReceiveProps
组件上添加Chart.js
。
class Chart extends Component {
constructor(props) {
...
}
componentWillReceiveProps(nextProps) {
if (nextProps.chartType !== this.state.chartType) {
this.setState({chartType: nextProps.chartType})
}
}
}
为防止误解,上面提到的Chart.js
表示有问题的Chart.js
文件。