我是 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;
最佳答案
我将解决您发布的代码的几个问题。这里的目的是提供一个指南,而不深入解释每个部分。
Redux
Redux (和 react-redux )用于管理应用程序的状态。它提供了一个中央存储,应保存渲染应用程序所需的所有数据和 mechanism当商店的状态发生变化时更新组件。
您的案例的功能流程是:
- 容器支架
- 它通过 AJAX 发出 API 调用
- 当调用返回时dispatch一个action ,例如
dataReceived
* - 一个reducer通过更新商店来处理操作
- 您的容器/组件应该是 connected通过其 props 使用商店中的新数据进行更新
- 组件使用新数据重新渲染
[* 您通常还希望跟踪请求的进度,以便在执行请求之前进行调度和操作(例如 dataRequested
),也许 dataRequestFailed
如果发生错误]
jQuery
你不需要它。
React 应该用于将 props 传递给子组件并更新 DOM。
为什么要通过 DOM 传递宽度和高度属性,而不是通过 this.props
直接访问它们在你的渲染代码中?
可以使用许多不同的库来发出 Ajax 请求。
D3
有一个few有关如何将 D3 与 React 集成的方法。 您可以让 D3 完成所有渲染,也可以使用人造 DOM 并使用 React 进行渲染。
您发布的代码使用 D3 进行渲染,但仅在 componentDidMount
中这样做。方法。您还应该 Hook componentDidUpdate
方法,以便您可以将更新的 props 传递给 D3。 Here这是关于如何实现这一目标的一篇很好的文章。
最小 POC
我添加了上面描述的流程的简单演示
注释
connect
用于创建一个组件,该组件订阅商店中的更改,并在商店更改时自动更新。
mapStateToProps
定义应将商店的哪些属性传递给容器。
mapDispatchToProps
将操作绑定(bind)到 dispatch
允许通过 this.props.<action_name>
在容器内轻松使用的方法.
reducer 是使用 switch 语句编写的,以说明 reducer 可以处理多个操作。
// 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>
关于javascript - 如何将 Redux 与 Reactjs 结合使用 - 以这个基本的 d3.js 图表为例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43544947/