Is there any proper way to integrate d3.js graphics into Facebook React application?
Yes, I know there is react-art, but it seems to be broken. The problem is that d3.js is all about mutating the browser DOM, and using it directly, say inside componentDidMount method, will not work properly, because all changes to browser DOM will not be reflected in React’s virtual DOM. Any ideas?
One strategy might be to build a black-box component that you never let React update.
The component lifecycle method shouldComponentUpdate() is intended to allow a component to determine on its own whether or not a rerender is necessary. If you always return false from this method, React will never dive into child elements (that is unless you call forceUpdate()), so this will act as a sort of firewall against React’s deep update system.
shouldComponentUpdate() {
return false;
}
Use the first call to render() to produce the container for the chart, then draw the chart itself with D3 within the componentDidMount() method. Then it just comes down to updating your chart in response to updates to the React component.
render() {
return <svg></svg>;
},
componentDidMount() {
d3.select(this.getDOMNode())
.call(chart(this.props));
},
shouldComponentUpdate(props) {
d3.select(this.getDOMNode())
.call(chart(props));
return false;
}
Note that you need to call your chart both in the componentDidMount() method (for the first render) as well as in shouldComponentUpdate() (for subsequent updates). Also note that you need a way to pass the component properties or state to the chart, and that they are context-specific: in the first render, they have already been set on this.props and this.state, but in later updates the new properties are not yet set on the component, so you need to use the function parameters instead.
componentDidMount() {
d3.select(this.getDOMNode())
.call(chart(this.props));
},
shouldComponentUpdate(props) {
d3.select(this.getDOMNode())
.call(chart(props));
return false;
}
React can also render the SVG elements directly. Here is an example:
React and D3 Integration
// Your React component
import React from 'react';
import { select } from 'd3';
const MyComponent = () => {
const svgRef = React.useRef();
React.useEffect(() => {
const svg = select(svgRef.current);
// D3 code to manipulate the SVG
}, []);
return <svg ref={svgRef} width={200} height={200}></svg>;
};
export default MyComponent;
You can use D3 as a utility – that is, DON’T use D3 to change the DOM. Rather, use D3 for its scales and axis stuff, but interpolate the outputs of those D3 functions into the HTML/SVG. Here’s an example:
// D3 code
scale = d3.time.scale()
.range([0, 100])
.clamp(true);
// React code
<rect
x="#{props.scale(start)}%"
width="#{Math.abs(props.scale(end) - props.scale(start))}%"
/>
I am using the useRef hook to save a reference to the D3 visualization so that I can control when only the chart needs to be updated when there is other “stuff” on the page. In D3, I am using the reusable chart pattern outlined in this tutorial:
// React code
import React, { useEffect, useRef, useState } from 'react';
import { select } from 'd3';
const MyComponent = () => {
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>
</>
);
};
// D3 code
const d3Chart = () => {
// Chart logic here
};
export default MyComponent;
Now you have different strategies to integrate D3.js graphics into your Facebook React application. Choose the one that suits your needs and start creating stunning visualizations!