【发布时间】:2017-11-28 17:21:53
【问题描述】:
我正在开发我的第一个 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>} 不正确,但这是我第一次尝试合并我拥有的两种技术。我的第二次尝试是用通用图表组件 (<Chart />) 替换 {chartToBeDisplayed},并将 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 的新手,希望得到一些指导/澄清。谢谢!
【问题讨论】: