将数据从基于React的组件传递到Vanilla js类

时间:2019-09-18 12:37:09

标签: reactjs d3.js

我正在尝试将数据从react类的基础组件传递到vanillajs类,以便此类可以呈现D3条形图, 我试图将数据从react组件传递到vanilla类的承包商,当我尝试对其进行日志记录时,我有vanilla类中的可用数据,但是当我想在方法调用d3中调用data变量时.data()为空,这是代码

反应课

//进口。.

      const _data = []
      const firebaseConfig = {
          //configuration .. 
      };
      // Initialize Firebase
      firebase.initializeApp(firebaseConfig);
      const db = firebase.firestore()
      class TableOfD3 extends Component {
        constructor(){
          super()
          this.svgId = `SVG_${uuid()}`


        }
       getData(){ 
       db.collection('db').get().then( res=>{
            res.docs.forEach(doc => {
             _data.push(doc.data())
            })
        }
        componentDidMount(){

          this.start()
        }

        componentDidUpdate(){

          this.start()

        }
        start(){
          this._graph = new D3TableEngine('#' + this.svgId,_data)
          this._graph.start()
          }

        render() {

          return (
            <div>
              <svg  id={this.svgId}></svg>
            </div>
          );
        }
      }

      export default TableOfD3;

// vanillajs类

export default class D3TableEngine {
        constructor(svgId, passedData) {

          this._svg = d3.select(`${svgId}`);
          this._svg.attr('width', _WIDTH)
          this._svg.attr('height', _HEIGHT)
          this._passedData = passedData
          }


        start() {
          const self = this;
          var _g = self._svg;

          const graphWidth = _WIDTH - _MARGIN.left - _MARGIN.right
          const graphHeight = _HEIGHT - _MARGIN.top - _MARGIN.bottom

          const graph = _g.append('g')
          .attr('width', graphWidth)
          .attr('height', graphHeight)
          .attr('transform', `translate(${_MARGIN.left + 20}, ${_MARGIN.top})`)

          const xAxisGroup = graph.append('g')
            .attr('transform', `translate(0,${graphHeight })`)
          const yAxisGroup = graph.append('g')

          const yScale = d3.scaleLinear()
              .domain([0,d3.max(self._passedData, (d) => d.orders)])
              .range([graphHeight,0])

          const xScale = d3.scaleBand()
              .domain(self._passedData.map((el) => el.name))
              .range([0,500])
              .paddingInner(0.2)
              .paddingOuter(0.2)

          const rects = graph.selectAll("rect").data(self._passedData);

              rects
            .attr("x", (d)=> xScale(d.name))
            .attr("y", (d) => yScale( d.orders))
            .attr("height", (d)=> graphHeight - yScale( d.orders))
            .attr("width", xScale.bandwidth)
            .attr('fill', 'blue')

          rects
            .enter()
            .append("rect")
            .attr("x", (d)=> xScale(d.name))
            .attr("y", (d) => yScale( d.orders))
            .attr("height", (d)=> graphHeight - yScale( d.orders ))
            .attr("width", xScale.bandwidth)
            .attr('fill', 'blue')

            const xAxis = d3.axisBottom(xScale)
            xAxisGroup.call(xAxis)
            const yAxis = d3.axisLeft(yScale)
              .ticks(5)
              .tickFormat((d) => 'Orders  ' +d  )
            yAxisGroup.call(yAxis)

            xAxisGroup.selectAll('text')
            .attr('transform', 'rotate(-40)' )
              .attr('text-anchor', 'end')
            } )
        }

        refresh() {}
      }

1 个答案:

答案 0 :(得分:1)

我重新编写了您的React类,因为您做了很多被认为是反模式的事情。通常,您想在this.state中尽力而为。否则,您会错过React的主要优势-并且在变量更改时可以最佳地重新呈现DOM。我认为您可能遇到的主要问题是您正在从componentDidUpdate()更新DOM,这将触发另一个更新。它会无限继续并崩溃。我会强烈建议D3TableEngine重构为React组件而不是普通的JS类。挑战在于,您编写d3组件的方式必须销毁并为每个渲染器重新创建,这是一个问题,因为React除了重新创建之外都不知道要做什么。

import React, { Component } from 'react';

class TableOfD3 extends Component {
  constructor() {
    super();

    const firebaseConfig = {
      //configuration .. 
    };
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
    const db = firebase.firestore();

    this.state = {
      svgId: `SVG_${uuid()}`,
      data: [],
      db: db
    };
  }

  componentDidMount() {
    const response = await this.state.db.collection('db').get();
    const data = response.docs.map(doc => doc.data());

    this.setState({
      data
    });
  }

  componentDidUpdate() {
  }

  render() {
    return (
      <div>
        <D3TableEngine
          id={this.state.svgId}
          data={this.state.data}
        />
      </div>
    );
  }
}

更新:我尝试将d3类重构为React组件。这里重要的部分是ref,让您获得对该元素的引用,以便redraw可以在右边的svg元素上执行所有d3代码。然后,在componentDidMountcomponentDidUpdate内部,您必须调用redraw。但是,我将重构redraw方法,以将要更改的部分从不会更改的部分中分解出来(例如:将图块移动到其他函数中,并在componentDidUpdate中进行调用)。我们这样做是为了使React能够按预期执行,并且仅更新DOM中已更改的元素。如果您需要其他帮助,可以查看此jsfiddle example / medium article

const MARGIN = 0;
const WIDTH = 0;
const HEIGHT = 0;

class D3TableEngine extends Component {

  componentDidMount() {
    redraw();
  }

  componentDidUpdate() {
    redraw();
  }

  redraw = () => {
    this.svg = d3.select(this.svg);
    const graphWidth = WIDTH - MARGIN.left - MARGIN.right
    const graphHeight = HEIGHT - MARGIN.top - MARGIN.bottom

    const graph = this.svg.append('g')
      .attr('width', graphWidth)
      .attr('height', graphHeight)
      .attr('transform', `translate(${_MARGIN.left + 20}, ${_MARGIN.top})`)

    const xAxisGroup = graph.append('g')
      .attr('transform', `translate(0,${graphHeight})`)

    const yAxisGroup = graph.append('g')

    const yScale = d3.scaleLinear()
      .domain([0, d3.max(props.data, (d) => d.orders)])
      .range([graphHeight, 0])

    const xScale = d3.scaleBand()
      .domain(props.data.map((el) => el.name))
      .range([0, 500])
      .paddingInner(0.2)
      .paddingOuter(0.2)

    const rects = graph.selectAll("rect").data(props.data);

    rects
      .attr("x", (d) => xScale(d.name))
      .attr("y", (d) => yScale(d.orders))
      .attr("height", (d) => graphHeight - yScale(d.orders))
      .attr("width", xScale.bandwidth)
      .attr('fill', 'blue')

    rects
      .enter()
      .append("rect")
      .attr("x", (d) => xScale(d.name))
      .attr("y", (d) => yScale(d.orders))
      .attr("height", (d) => graphHeight - yScale(d.orders))
      .attr("width", xScale.bandwidth)
      .attr('fill', 'blue')

    const xAxis = d3.axisBottom(xScale)

    xAxisGroup.call(xAxis)

    const yAxis = d3.axisLeft(yScale)
      .ticks(5)
      .tickFormat((d) => 'Orders  ' + d)

    yAxisGroup.call(yAxis)

    xAxisGroup.selectAll('text')
      .attr('transform', 'rotate(-40)')
      .attr('text-anchor', 'end')
  }

  render() {
    return (
      <svg
        id={this.props.svgId}
        width={WIDTH}
        height={HEIGHT}
        ref={el => (this.svg = d3.select(el))}
      >
      </svg>
    );
  }
}