简体   繁体   中英

d3 v4 + react + es6 + crossfilter: Selection.exit().remove() not working

I'm using crossfilter.js and d3.js v4 with ES6 style React in an attempt to make dimensional charts with context brushing. Essentially, I took this example and converted it to ES6.

The problem I have is selection.exit().remove() is not working such that on each redraw, more and more circles are appended to the svg g element. Redraws are triggered when a brush is created. I checked by running

selection.exit()
  .attr('class', d => {
    console.log('anything');
    return 'anything';
  })
  .remove();

but nothing was outputted so I'm thinking my data selection is not valid.

Here is the context:

componentDidMount() {
  const el = ReactDOM.findDOMNode(this); // the mounted svg element

  const svg = d3.select(el)
    .attr('width', 300)
    .attr('height', 500);

  const g = svg.append('g')
    .attr('transform', 'translate(32,32)');

  let circles = g.append('g')
    .selectAll('circle');

  const brushGroup = g.append('g')
    .attr('class', 'brush')
    .call(brush);

  function redraw() {
    const all = group.all(); // crossfilter group returns an array

    xAxisGroup.call(xAxis);
    yAxisGroup.call(yAxis);

    circles = circles.data(all, d => d); // keyFn for data constancy

    circles.enter().append('circle')
      .attr('r', radius)
      .attr('cx', plotX) // radius + plotX/Y are simple curry functions
      .attr('cy', plotY);

    circles.exit().remove(); // not working!!
  }

  redraw();
}

I am also aware of this change in d3-selection in v4 but I'm not super confident which lines are my update and which ones are my update + enter .

Any help would be much appreciated. Thank you!

I suspect the problem is one of 2 things. Probably #1 below. It's a bit hard to tell with out a working example to test things out in, but here you go:

  1. I believe that the selectAll and the data join should happen together in the redraw function. Because you never redo the selectAll in the redraw function, your selection will never contain any elements. If you check your enter and exit selections in your redraw function, your enter selection will always contain all your data points because the underlying selection is empty.

  2. That your data key function returns an object. Since the object is the result of Crossfilter's group.all , they should be comparable by reference, but it would be safer to do circles.data(all, d => d.key) .

The solution should be to do something like:

componentDidMount() {
  const el = ReactDOM.findDOMNode(this); // the mounted svg element

  const svg = d3.select(el)
    .attr('width', 300)
    .attr('height', 500);

  const g = svg.append('g')
    .attr('transform', 'translate(32,32)');

  let circlesGroup = g.append('g'); // Just hang on to the group

  const brushGroup = g.append('g')
    .attr('class', 'brush')
    .call(brush);

  function redraw() {
    const all = group.all(); // crossfilter group returns an array

    xAxisGroup.call(xAxis);
    yAxisGroup.call(yAxis);

    let circles = circlesGroup // Do the selection and data join here
      .selectAll('circle')
        .data(all, d => d.key); // Have the key function return a key, not an object

    circles.enter().append('circle')
      .attr('r', radius)
      .attr('cx', plotX) // radius + plotX/Y are simple curry functions
      .attr('cy', plotY);

    circles.exit().remove();
  }

  redraw();
}

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