我是Reactjs的新手 - 主要是通过整合Redux。我很想学习如何改进下面的代码库以使用Redux - 所以要了解这个当前示例有什么问题以及如何完全清理它 - 添加Redux - 并解释为什么要使用Redux及其主要目的。
所以这里是饼图的一些测试json数据
var data = [{
"label": "Belleville Brewing Company",
"value": 1233
}, {
"label": "Kew Brewery",
"value": 345
}, {
"label": "Laines Brewery (Four Thieves)",
"value": 6786
}, {
"label": "Sultan Brewery",
"value": 678
}, {
"label": "The Wimbledon Brewery Company Limited",
"value": 45
}];
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
var $ = require("jquery");
import PieChart from './modules/7_pieChart/PieChart';
var MultipleComponents = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: '',
rawData: '',
config: ''
};
},
componentDidMount: function () {
var config = [];
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url,
rawData: lastGist,
config: config
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
getLayers: function(data){
var items = [];
var j = 0;
items.push( <PieChart
key="5"
width="350"
height="350"
radius="200"
innerradius="120"
serviceApi=""/> );
return items;
},
render: function () {
var config = this.state.config;
console.log("config", config);
return (
<div className="apps">
{this.getLayers(config[0])}
</div>
);
}
});
ReactDOM.render(
<MultipleComponents source="https://api.github.com/users/octocat/gists" />,
document.getElementById('root')
);
//饼图js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
var $ = require("jquery");
var d3 = require("d3");
import './PieChart.css';
class PieChart extends Component {
componentDidMount() {
var $this = $(ReactDOM.findDOMNode(this));
var data = [{
"label": "Belleville Brewing Company",
"value": 1233
}, {
"label": "Kew Brewery",
"value": 345
}, {
"label": "Laines Brewery (Four Thieves)",
"value": 6786
}, {
"label": "Sultan Brewery",
"value": 678
}, {
"label": "The Wimbledon Brewery Company Limited",
"value": 45
}];
var w = $this.data("width");
var h = $this.data("height");
var ir = $this.data("innerradius");
var r = $this.data("radius");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
//var colores_g = ["#47abd5", "#005a70", "#f5a0a3", "#ff7276", "#a9a19c", "#d0743c", "#ff8c00"];
return colores_g[n % colores_g.length];
}
var radius = Math.min(w, h) / 3;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var labelArc = d3.svg.arc()
.outerRadius(radius - r)
.innerRadius(radius - ir);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var chart = d3.select($this[0]).append("svg:svg")
.attr("class", "chart")
.attr("width", w - (w/3))
.attr("height", h)
.append("svg:g")
.attr("class", "piechart")
.attr("transform", "translate(20,"+h/3+")");
var path_group = chart.append("svg:g")
.attr("class", "path_group")
.attr("transform", "translate(90," + ((h / 4) - 20) + ")");
var padding = 45;
var legendPaddingTop = 30;
var legend = d3.select($this[0]).append("svg:svg")
.attr("class", "legend")
.attr("width", w/2)
.attr("height", h)
.append("svg:g")
.attr("class", "legendsection")
.attr("transform", "translate(" + ((w/4) + padding) + "," + legendPaddingTop + ")");
var label_group = legend.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + (-(w / 3) + 20) + "," + 0 + ")");
var legend_group = legend.append("svg:g")
.attr("class", "legend_group")
.attr("transform", "translate(" + (-(w / 3) - 100) + "," + 0 + ")");
var g = path_group.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return colores_google(i);
});
var legendHeight = legendPaddingTop;
var ySpace = 18;
var labelPadding = 3;
//draw labels
var labels = label_group.selectAll("text.labels")
.data(data);
labels.enter().append("svg:text")
.attr("class", "labels")
.attr("dy", function(d, i) {
legendHeight+=ySpace;
return (ySpace * i) + labelPadding;
})
.attr("text-anchor", function(d) {
return "start";
})
.text(function(d) {
return d.label;
});
labels.exit().remove();
//draw labels
//draw legend
var legend = legend_group.selectAll("circle").data(data);
legend.enter().append("svg:circle")
.attr("cx", 100)
.attr("cy", function(d, i) {
return ySpace * i;
})
.attr("r", 7)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
});
legend.exit().remove();
//draw legend
//reset legend height
//console.log("optimum height for legend", legendHeight);
$this.find('.legend').attr("height", legendHeight);
function type(d) {
d.value = +d.value;
return d;
}
}
render() {
return (
<div className="piechart" data-role="piechart" data-width={this.props.width} data-height={this.props.height} data-radius={this.props.radius} data-innerradius={this.props.innerradius}
data-data={this.props.data}>
</div>
);
}
};
export default PieChart;
答案 0 :(得分:2)
我将解决您发布的代码的几个问题。这里的目的是提供一个指南,而不是深入解释每个部分。
Redux(和react-redux)用于管理应用程序的状态。它提供了一个中央存储,它应该包含呈现应用程序所需的所有数据,以及mechanism在商店状态发生变化时更新组件。
您案例中的功能流程为:
dataReceived
* [*您通常也希望跟踪请求的进度,因此您将在执行请求之前发送和执行操作(例如dataRequested
),如果发生错误,则可能dataRequestFailed
你不需要它。
应该使用React将props传递给子组件并更新DOM。
为什么要通过DOM传递宽度和高度道具,而不是直接通过渲染代码中的this.props
访问它们?
Ajax请求可以通过许多不同的库来完成。
有关如何将D3与React集成的few方法。 您可以让D3完成所有渲染或使用faux-DOM并使用React渲染它。
您发布的代码使用D3进行渲染,但仅在componentDidMount
方法中进行渲染。您还应该挂钩componentDidUpdate
方法,以便将更新的道具传递给D3。 Here是关于如何实现这一目标的一篇很好的文章。
我添加了一个关于上面描述的流程的简单演示
备注的
connect
用于创建订阅商店中的更改的组件,并在商店更改时自动更新。
mapStateToProps
定义应将商店的哪些属性传递给容器。
mapDispatchToProps
将操作绑定到dispatch
方法,以便通过this.props.<action_name>
在容器内轻松使用。
reducer是使用switch语句编写的,用于说明reducers可以处理多于1个动作的事实。
// reducers.js
const reducer = (state = {data: []}, action) => {
switch(action.type) {
case 'DATA_RECEIVED':
return {...state, data: [...state.data, action.payload] }
}
return state;
}
// store.js
const store = Redux.createStore(reducer)
// actions.js
function dataReceived(payload) {
console.log('dataReceived action: ', payload);
return {
type: 'DATA_RECEIVED',
payload,
}
}
// pie_chart.js
class PieChart extends React.Component {
_update() {
const svg = d3.select(this.node).select('svg')
.attr('width', this.props.width)
.attr('height', this.props.height)
const y = d3.scale.ordinal().domain(this.props.data)
.rangePoints([20, this.props.height - 10])
const textItems = svg.selectAll('text').data(this.props.data)
textItems.enter().append('text').text(d => d)
textItems.transition().attr('y', (d, i) => y(d))
textItems.exit().remove()
}
componentDidMount() {
const svg = d3.select(this.node)
.append('svg')
this._update()
}
componentDidUpdate() {
this._update()
}
render() {
return (
<div class="container" ref={node => this.node = node}/>
)
}
}
// chart_container.js
function mapStateToProps(state) {
return { data: state.data }
}
function mapDispatchToProps(dispatch) {
return {
dataReceived: Redux.bindActionCreators(dataReceived, dispatch),
}
}
const ChartContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(class ChartContainer extends React.Component {
componentDidMount() {
let items = 3
const active = setInterval(() => { // simulate API call
this.props.dataReceived('data item ' + items);
if (items < 1) {
clearInterval(active)
}
items -= 1
}, 3000)
}
render() {
return (
this.props.data.length > 0 ?
<PieChart width={100} height={100} data={this.props.data} /> :
<div>no data yet</div>
)
}
})
// app.js
ReactDOM.render(
<ReactRedux.Provider store={store}>
<ChartContainer />
</ReactRedux.Provider>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.4/react-redux.js"></script>
<div id="root"></div>