简体   繁体   English

如何在Reactjs中使用Redux-以这个基本的d3.js图表​​为例

[英]How to use Redux with Reactjs - this basic d3.js chart as an example

I am new to Reactjs - mostly though with incorporating Redux. 我是Reactjs的新手-多数情况下都集成了Redux。 I am keen to learn how to improve the code base below to use Redux - so get to understand what is wrong with this current example and how to clean it up fully - with the addition of Redux - and an explanation as to why to use Redux and its main purpose. 我热衷于学习如何改进下面的代码库以使用Redux-因此,请理解这个当前示例的问题所在,以及如何彻底清除它(添加Redux)以及有关为什么使用Redux的解释及其主要目的。

So here is some test json data for the pie chart 所以这是饼图的一些测试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 //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')
);

//pie chart js //饼图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;

I'll address several issues with the code you posted. 我将解决您发布的代码的几个问题。 The purpose here is to provide a guide without explaining in depth each part. 此处的目的是提供指南,而无需深入解释每个部分。

Redux Redux

Redux (and react-redux ) is there to manage the state of your application. Redux (和react-redux )在那里管理应用程序的状态。 It provides a central store which should hold all the data needed to render your app and a mechanism to update components when the state of the store changes. 它提供了一个中央存储,该存储应存储呈现应用程序所需的所有数据,并提供一种在存储状态更改时更新组件的机制

The functional flow in your case would be: 您的情况下的功能流程为:

  1. container mounts 容器架
  2. it issues an API call via AJAX 它通过AJAX发出API调用
  3. when call returns dispatch an action , eg dataReceived * 当调用返回时调度一个动作 ,例如dataReceived *
  4. a reducer handles the action by updating the store 减速器通过更新商店来处理动作
  5. your container/component that should be connected is updated with the new data from the store via its props 您应该连接的容器/组件将通过其道具用商店中的新数据更新
  6. component re-renders with new data 用新数据重新渲染组件

[* you usually will want to track the request's progress as well so you will dispatch and action before you perform the request (eg dataRequested ) and perhaps dataRequestFailed if an error occurs ] [*您通常还希望跟踪请求的进度,因此您将在执行请求之前分派并采取行动(例如dataRequested ),如果发生错误,则可能是dataRequestFailed ]

jQuery jQuery的

You don't need it. 不用了

React should be used to pass props to child components and to update the DOM. 应使用React将prop传递给子组件并更新DOM。

Why are you passing the width and height props via the DOM instead of accessing them directly via this.props in your rendering code? 为什么要通过DOM传递width和height this.props ,而不是通过渲染代码中的this.props直接访问它们?

Ajax requests can be made with many different libraries that do just that. 可以使用许多不同的库来执行Ajax请求。

D3 D3

There are a few approaches on how to integrate D3 with React. 关于如何将D3与React集成的方法有几种 You can either let D3 do all the rendering or use a faux-DOM and render it with React. 您可以让D3完成所有渲染,也可以使用人造DOM并通过React进行渲染。

The code you posted renders using D3, but it does so only in the componentDidMount method. 您发布的代码使用D3进行渲染,但仅在componentDidMount方法中这样做。 You should hook also to the componentDidUpdate method so that you can pass updated props to D3. 您还应该挂钩到componentDidUpdate方法,以便可以将更新的道具传递给D3。 Here is a nice writeup on how this can be achieved. 是有关如何实现此目标的不错的文章。

Minimal POC 最小POC

I've added a simple demonstration of the flow I've described above 我已经添加了上述流程的简单演示

Notes 笔记

connect is used to create a component which is subscribed to changes in the store and will automatically update when the store changes. connect用于创建一个组件,该组件订阅商店中的更改,并在商店更改时自动更新。

mapStateToProps defines which properties of the store should be passed to the container. mapStateToProps定义应将商店的哪些属性传递给容器。

mapDispatchToProps bind actions to the dispatch method allowing for easy usage inside the container via this.props.<action_name> . mapDispatchToProps将动作绑定到dispatch方法,从而可以通过this.props.<action_name>在容器内部轻松使用。

The reducer is written using a switch statement to illustrate the fact that reducers can handle more than 1 action. 使用一个switch语句编写reducer,以说明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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM