D3 Responsive React TypeScript Boilerplate
Copy paste this to get a solid foundation for a responsive D3 React TypeScript chart. The dimensions of the chart are determined by the CSS controlling the chart-container, and the chart within will dynamically resize to accomodate that container.
import React, {useEffect, useRef, useState, useMemo} from 'react';
import * as d3 from 'd3';
interface Dimensions {
width: number;
height: number;
margins: number;
containerWidth: number | null;
containerHeight: number | null;
}
const SomeChart = () => {
const svgRef = useRef(null);
const svgContainer = useRef(null); // The PARENT of the SVG
// State to track width and height of SVG Container
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
// memoize full dimensions
const dimensions: Dimensions = useMemo(() => {
let dimensions = {
width: width,
height: height,
margins: 0,
containerWidth: null,
containerHeight: null,
};
dimensions.containerWidth = dimensions.width - dimensions.margins * 2;
dimensions.containerHeight = dimensions.height - dimensions.margins * 2;
return dimensions;
}, [width, height]);
// detect size of svg container
useEffect(() => {
const getSvgContainerSize = () => {
const newWidth = svgContainer.current?.clientWidth || 0;
setWidth(newWidth);
const newHeight = svgContainer.current?.clientHeight || 0;
setHeight(newHeight);
};
getSvgContainerSize();
window.addEventListener('resize', getSvgContainerSize);
return () => window.removeEventListener('resize', getSvgContainerSize);
}, []);
// memoize svg element
const svg = useMemo(() => {
return d3
.select(svgRef.current)
.classed('some-chart-svg', true)
.attr('width', dimensions.width)
.attr('height', dimensions.height);
}, [dimensions]);
// memoize container element
const container = useMemo(() => {
return svg
.append('g')
.classed('some-chart-inner-container', true)
.attr(
'transform',
`translate(${dimensions.margins}, ${dimensions.margins})`,
);
}, [svg, dimensions]);
// draw chart
useEffect(() => {}, [svg, container]);
return (
<div className="chart-container">
<div ref={svgContainer} className="chart-svg-container">
<svg ref={svgRef} />
</div>
</div>
);
};
export default SomeChart;