无法正确删除D3元素

时间:2018-10-05 19:48:51

标签: reactjs d3.js

我正在尝试使用remove方法删除条形图并插入一个新的条形图。当我在选择上执行remove()时,SVG内容将被清空,但是svg元素仍位于DOM中,并占用了屏幕空间。新版本的SVG被添加到空白版本的右侧。当我改为在包含的div上执行remove()时,svg完全消失了,但是当我调用代码以插入新版本时,不会创建新图表。

D3_BarChart.update = (el, data, configuration, chart) => {
  // d3.select(el).remove();
  chart.remove();

  D3_BarChart.create(el, dataSubset, configuration);
};

我正在https://www.smashingmagazine.com/2018/02/react-d3-ecosystem/中描述的“生命周期方法包装”之后从React中执行D3代码,因此应该将两个库完全分开,并且不要相互运行。

D3代码

// d3 is not a top level export
import * as d3 from 'd3';

// Simple Bar Chart from D3 v4 example at https://bl.ocks.org/d3noob/bdf28027e0ce70bd132edc64f1dd7ea4
const D3_BarChart = {};

/* eslint-disable prefer-arrow-callback */
/* eslint-disable fun-names */

const dataSubset = `salesperson,sales
Bob,33
Robin,12`;

D3_BarChart.create = (el, data, configuration) => {
  // D3 Code to create the chart

  // Styling
  el.style.fill = 'steelblue';

  data = `salesperson,sales
  Bob,33
  Robin,12
  Anne,41
  Mark,16
  Joe,59
  Eve,38
  Karen,21
  Kirsty,25
  Chris,30
  Lisa,47
  Tom,5
  Stacy,20
  Charles,13
  Mary,29`;

  // set the dimensions and margins of the graph
  const margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 40
  };

  const width = 960 - margin.left - margin.right;

  const height = 500 - margin.top - margin.bottom;

  // set the ranges
  const x = d3
    .scaleBand()
    .range([0, width])
    .padding(0.1);
  const y = d3.scaleLinear().range([height, 0]);

  // append the svg object to the body of the page
  // append a 'group' element to 'svg'
  // moves the 'group' element to the top left margin
  const svg = d3
    .select(el)
    .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
    .append('g')
    .attr('transform', `translate(${margin.left},${margin.top})`);

  // get the data
  const parsedData = d3.csvParse(data);

  // format the data
  parsedData.forEach(function (d) {
    d.sales = +d.sales;
  });

  // Scale the range of the data in the domains
  x.domain(
    parsedData.map(function (d) {
      return d.salesperson;
    })
  );
  y.domain([
    0,
    d3.max(parsedData, function (d) {
      return d.sales;
    })
  ]);

  // append the rectangles for the bar chart
  svg
    .selectAll('.bar')
    .data(parsedData)
    .enter()
    .append('rect')
    .attr('class', 'bar')
    .attr('x', function (d) {
      return x(d.salesperson);
    })
    .attr('width', x.bandwidth())
    .attr('y', function (d) {
      return y(d.sales);
    })
    .attr('height', function (d) {
      return height - y(d.sales);
    });

  // add the x Axis
  svg
    .append('g')
    .attr('transform', `translate(0,${height})`)
    .call(d3.axisBottom(x));

  // add the y Axis
  svg.append('g').call(d3.axisLeft(y));

  return svg;
};

D3_BarChart.update = (el, data, configuration, chart) => {
  // D3 Code to update the chart
  console.log('update el', el);
  console.log('update chart', chart);
  d3.select(el).remove();
  // chart.remove();
  D3_BarChart.create(el, dataSubset, configuration);
};

D3_BarChart.destroy = () => {
  // Cleaning code here
};

export default D3_BarChart;

反应容器

/* eslint-disable no-undef */

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import Explore from '../components/Explore';
import D3_BarChart from '../components/d3/D3_BarChart';
import { resetErrorMessage } from '../actions';

class App extends Component {
  static propTypes = {
    // Injected by React Redux
    errorMessage: PropTypes.string,
    resetErrorMessage: PropTypes.func.isRequired,
    inputValue: PropTypes.string.isRequired,
    // Injected by React Router
    children: PropTypes.node
  };

  constructor(props) {
    super(props);

    this.setBarChartContainerRef = this.setBarChartContainerRef.bind(this);
  }

  componentDidMount() {
    // D3 Code to create the chart
    this.d3_barChart = D3_BarChart.create(
      this.d3_barChartContainerRef,
      this.props.data,
      this.props.config
    );

    setTimeout(() => {
      D3_BarChart.update(this.d3_barChartContainerRef, this.props.data, this.props.config, this.d3_barChart);
    }, 200);
  }

  handleDismissClick = e => {
    this.props.resetErrorMessage();
    e.preventDefault();
  };

  handleChange = nextValue => {
    this.props.history.push(`/${nextValue}`);
  };

  renderErrorMessage() {
    const { errorMessage } = this.props;
    if (!errorMessage) {
      return null;
    }

    return (
      <p style={{ backgroundColor: '#e99', padding: 10 }}>
        <b>{errorMessage}</b> <button onClick={this.handleDismissClick}>Dismiss</button>
      </p>
    );
  }

  setBarChartContainerRef(element) {
    this.d3_barChartContainerRef = element;
  }

  render() {
    const { children, inputValue } = this.props;
    return (
      <div className="d3-container" ref={this.setBarChartContainerRef.bind(this)}>
        {/*  <D3_BarChart value={inputValue} onChange={this.handleChange} /> */}
        {/*         <Explore value={inputValue} onChange={this.handleChange} />*/}
      </div>
    );
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    errorMessage: state.errorMessage,

    inputValue: ownProps.location.pathname.substring(1)
  };
};

export default withRouter(
  connect(
    mapStateToProps,
    {
      resetErrorMessage
    }
  )(App)
);

1 个答案:

答案 0 :(得分:0)

create方法需要返回d3.select(el).select('svg'),然后chart.remove()起作用。