简体   繁体   中英

React component called twice (AJAX call async)

What I want to do :

I would like to create a pie chart with the javascript library d3.js. I get data from my server with an AJAX call, and then create my piechart with it.

What I've done :

For this I created 2 react component : one which implements the ajax call and pass his state to the other component which create the pie chart with the data in props.

First component :

import React from 'react';
import Pie from './pie.jsx';

class RingChart extends React.Component {
  loadPieChart() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: (data) => {
        let d3data = Object.keys(data)
          .filter(key => key!= 'pourcentage')
          .map(key => { return {name: key, value: data[key]} });
        this.setState({dataPie: d3data, percent: data['pourcentage']});
      },
      error: (xhr, status, err) => {
        console.error(this.props.url, status, err.toString());
      }
    });
  }

  constructor(props) {
    super(props);
    this.state = {dataPie: [], percent: 0};
  }

  componentDidMount() {
    this.loadPieChart();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  }

  render() {
    return (
    <div className="chart">
      <Pie size="400" data={this.state.dataPie} percent={this.state.percent}></Pie>
    </div>
    );
  }
}

export default RingChart;

Here my second component which create my pie chart :

import React from 'react';

class Pie extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidUpdate() {
    let size = this.props.size;
    const data = this.props.data;
    let percent = this.props.percent;

    console.log(data);

    var margin = {top: 20, right: 20, bottom: 20, left: 20};
    let width = size - margin.left - margin.right;
    let height = width - margin.top - margin.bottom;

    var chart = d3.select(".ring")
                    .append('svg')
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                   .append("g")
                    .attr("transform", "translate(" + ((width/2)+margin.left) + "," + ((height/2)+margin.top) + ")");

    var radius = Math.min(width, height) / 2;

    var color = d3.scale.ordinal()
        .range(["#313F46", "#4DD0E1"]);

    var arc = d3.svg.arc()
        .outerRadius(radius)
        .innerRadius(radius - 20);

    var pie = d3.layout.pie()
        .sort(null)
        .startAngle(1.1*Math.PI)
        .endAngle(3.1*Math.PI)
        .value(function(d) { return d.value; });


    var g = chart.selectAll(".arc")
      .data(pie(data))
      .enter().append("g")
      .attr("className", "arc");
      function tweenPie(b) {
        var i = d3.interpolate({startAngle: 1.1*Math.PI, endAngle: 1.1*Math.PI}, b);
        return function(t) { return arc(i(t)); };
      }
    g.append("path")
        .attr("fill", function(d, i) { return color(i); })
      .transition()
        .ease("exp")
        .duration(600)
        .attrTween("d", tweenPie);

    g.append("text")
          .attr("dy", ".35em")
          .style("text-anchor", "middle")
          .style("color", "#263238")
          .style("font-size", "55px")
          .attr("className", "text-pie")
          .text(function(d) { return percent+'%'; });

  }

  render() {
    return (<div className="ring" style={{textAlign:'center'}}></div>)
  }
}

export default Pie;

My problem :

Nothing is created ... The console.log(data) in my second component return firstly an empty array and secondly my array with the correct values. It's weird that nothing is created, but even if it was created, my component Pie is call twice.

How can I call my component only once ? Why my component is not created ? How can I do for that my pie chart will be update automatically when new values appears on my server ?

thank's a lot, I'm discovering react.js, a lot of basic's notions is unknow for me.

EDIT : Ok now my component is created, I had forgotten the ".ring" in my line : var chart = d3.select("ring") -_-.

But as I said before, now two component are created (one empty, and one correct). Sometimes I have two components created correctly. It depends on the AJAX call ... How can I solve the problem of the async AJAX call ?

React basically takes the props and state of a component and when these changes it updates the component (it executes the render function again and then diffs this with your actual DOM).

You tap into the lifecycle with the ComponentDidUpdate method. This will be called every time React updates a component . Therefore, the first time it's called your array is empty (your ajax call hasn't returned). Then your call returns, which results in the array being filled. React updates your component and you get the updated data. The fact you see the log twice is simply because, according to React, your component should be updated: Once with an empty array, and once with the results in the filled array.

To see if you actually create two components, check out either the constructor or the ComponentDidMount methods, logging in there should only be called once per intended creation of the component.

To summarize: it's no problem that the ComponentDidUpdate call is called more than once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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