是的,我知道有反应艺术,但是it seems to be broken。
问题是d3.js是关于改变浏览器DOM的,并且直接使用它,比如说在componentDidMount方法中,将无法正常工作,因为对浏览器DOM的所有更改都不会反映在React的虚拟DOM中。有什么想法吗?
答案 0 :(得分:59)
一种策略可能是构建一个永远不会让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.props
和this.state
上设置,但是在以后的更新中,尚未在组件上设置新属性,因此您需要使用函数参数。
请参阅jsfiddle here。
答案 1 :(得分:6)
React也可以直接渲染SVG元素。以下是一个示例:Ways of Integrating React.js and D3
答案 2 :(得分:2)
您可以使用D3作为实用程序 - 也就是说,DON使用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的选择。
答案 3 :(得分:0)
我正在使用 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;