Skip to main content

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;

Comments