繁体   English   中英

有没有什么合适的方法可以将 d3.js 图形集成到 Facebook React 应用程序中?

[英]Is there any proper way to integrate d3.js graphics into Facebook React application?

是的,我知道有反应艺术,但它似乎被打破了

问题是 d3.js 是关于改变浏览器 DOM 的,直接使用它,比如在 componentDidMount 方法内部,将无法正常工作,因为对浏览器 DOM 的所有更改都不会反映在 React 的虚拟 DOM 中。 有任何想法吗?

一种策略可能是构建一个永远不会让 React 更新的黑盒组件。 组件生命周期方法shouldComponentUpdate()旨在允许组件自行确定是否需要重新渲染。 如果你总是从这个方法返回false ,React 将永远不会深入到子元素(也就是说,除非你调用forceUpdate() ),所以这将充当一种针对 React 深度更新系统的防火墙。

使用对render()的第一次调用来生成图表的容器,然后在componentDidMount()方法中使用 D3 绘制图表本身。 然后归结为更新图表以响应 React 组件的更新。 尽管您可能不应该shouldComponentUpdate()做类似的事情,但我认为没有真正的理由您不能继续从那里调用 D3 更新(请参阅 React 组件的_performUpdateIfNecessary()的代码)。

所以你的组件看起来像这样:

React.createClass({
    render: function() {
        return <svg></svg>;
    },
    componentDidMount: function() {
        d3.select(this.getDOMNode())
            .call(chart(this.props));
    },
    shouldComponentUpdate: function(props) {
        d3.select(this.getDOMNode())
            .call(chart(props));
        return false;
    }
});

请注意,您需要在componentDidMount()方法(对于第一次渲染)以及shouldComponentUpdate() (对于后续更新)中调用图表。 另请注意,您需要一种将组件属性或状态传递给图表的方法,并且它们是特定于上下文的:在第一次渲染中,它们已经在this.propsthis.state上设置,但在稍后更新组件上尚未设置新属性,因此您需要改用函数参数。

请参阅此处的 jsfiddle。

React 也可以直接渲染 SVG 元素。 这是一个例子:集成 React.js 和 D3 的方法

您可以将 D3 用作实用程序——也就是说,不要使用 D3 来更改 DOM。 相反,使用 D3 作为它的比例和轴的东西,但将这些 D3 函数的输出插入到 html/svg 中。

这是我的项目中的一个示例。

 scale = d3.time.scale()
   .range([ 0, 100 ])
   .clamp(true)

...通过道具传递给组件...

React.DOM.rect({
          x: "#{props.scale(start)}%"
          width: "#{Math.abs( props.scale(end) - props.scale(start)) }%"
         })

其中 React 将数据绑定到元素,而不是 D3 的选择。

我正在使用 useRef -hook 来保存对 D3 可视化的引用,以便在页面中有其他“东西”时,我可以控制何时只需要更新图表。

在 D3 中,我使用了可重用的图表模式,例如: https : //www.toptal.com/d3-js/towards-reusable-d3-js-charts

import React, {useEffect, useRef, useState} from 'react';

import { select } from "d3";

const TestArea = () => {

    const [data, setData] = useState(['red']);
    const refElement = useRef(null);
    const visFunction = useRef(null);

    const initVis = () => {
        visFunction.current = d3Chart().data(data)
        select(refElement.current).call(visFunction.current)         
    }

    const updateVis = () => {
        visFunction.current.data(data);      
    }

    useEffect(() => {

        if(data && data.length){

            if(visFunction.current === null)
                initVis()
            else
                updateVis()

        }
        
    }, [data])

    return (
        <>
            <div ref={refElement} className="d3container"></div>
            <button onClick={() => setData(['blue'])}>Update</button>
        </>
    );
};


const d3Chart = () => {

    let svg;
    let gElem;
    let circles;

    let data = []
    let width = 200;
    let height = 200;

    let updateData;

    function chart(selection){

        selection.each(function(){

            svg = select(this)
                .append('svg')

            gElem = svg
                .append('g')
                .attr('transform', ` translate(${width/2},${height/2})`)

            circles = gElem
                .selectAll("circle")
                .data(data)
                .enter()
                .append("circle")
                    .style("stroke", "none")
                    .style("fill", d => d)
                    .attr("r", 10)
                    .attr("cx", 0)
                    .attr("cy", 0)

            updateData = function() {

                circles = gElem
                    .selectAll("circle")
                    .data(data)
                        .style("fill", d => d)

            }

        })
    }

    chart.data = function(val){

        if(!arguments.length) return data;

        data = val;

        if(typeof updateData === 'function')
            updateData();
   
        return chart

    }

    return chart

}


export default TestArea;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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