简体   繁体   中英

Updating d3 elements in React?

I am trying to udpate this bubblechart base on my search input in an input bar. Right now, I put all the d3 code in a Bubble.js, and then in my app.js, I have a searchinput element that will filter my data to be displayed, and then in my Bubble's state, I set its data equal to the filtered data (named RoadmapData). However, my bubble chart is not updating. Actually, each time I type something in, another bubble chart renders, so if I type in 3 letters there are three same and unfiltered bubble charts .

Here is my code right now:

import React, { Component } from "react";
import * as d3 from "d3";

class Bubble extends Component {
  constructor(props) {
    super(props);
    this.state = {
      dataset: { children: this.props.RoadmapData }
    };
  }

  componentWillReceiveProps(nextProps) {
    var diameter = 600;
var color = d3.scaleOrdinal(d3.schemeCategory10);

var bubble = d3
  .pack(this.state.dataset)
  .size([diameter, diameter])
  .padding(1.5);

var svg = d3
  .select("body")
  .append("svg")
  .attr("width", diameter)
  .attr("height", diameter)
  .attr("class", "bubble");

var nodes = d3.hierarchy(this.state.dataset).sum(function(d) {
  return d.Count;
});

var node = svg
  .selectAll(".node")
  .data(bubble(nodes).descendants())
  .enter()
  .filter(function(d) {
    return !d.children;
  })
  .append("g")
  .attr("class", "node")
  .attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });
node.append("title").text(function(d) {
  return d.Name + ": " + d.Count;
});

node
  .append("circle")
  .attr("r", function(d) {
    return d.r;
  })
  .style("fill", function(d, i) {
    return color(i);
  });

node
  .append("text")
  .attr("dy", ".2em")
  .style("text-anchor", "middle")
  .text(function(d) {
    return d.data.Name.substring(0, d.r / 3);
  })
  .attr("font-family", "sans-serif")
  .attr("font-size", function(d) {
    return d.r / 5;
  })
  .attr("fill", "white");

node
  .append("text")
  .attr("dy", "1.3em")
  .style("text-anchor", "middle")
  .text(function(d) {
    return d.data.Count;
  })
  .attr("font-family", "Gill Sans", "Gill Sans MT")
  .attr("font-size", function(d) {
    return d.r / 5;
  })
  .attr("fill", "white");
  }

  render() {
return <div>{this.node}</div>;
  }
}

export default Bubble;

I am a beginner in d3, but I feel like the problem is either that I am using lifecycle methods wrong ( I used componentWillReceiveProps here, when I used componentMount() and typed in my search bar, nothing changed. Or maybe I shouldn't return this.node? Thanks in advance.

What's happening is that each time your component is re-rendered, the componentWillReceiveProps function is creating a new instance of the visualization and adding it to the <div> node created by React, via the calls to node.append . You can avoid this by removing the previously created instances via the d3 remove() function .

If you're willing to introduce another library, there's one called React Faux Dom that would allow you to avoid all the React lifecycle stuff and just do everything in the render() function as normal. It may simplify things a little.

You also mention that the chart is unfiltered every time - this is because you only assign the data prop to state in the constructor - this only runs once when your component is first created. In this case, it looks like you can safely remove this and read the data from this.props.RoadmapData every time (It doesn't appear that you are making any changes to the state inside the component).

You'd just need to add a line at the beginning of render() to get the data into the right format for d3, something like:

var roadmapDataForD3 = { children: this.props.RoadmapData };

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